From 7aca9d9c70a10dcb517665d9b7f832f0e4a4dd51 Mon Sep 17 00:00:00 2001 From: Steven Date: Sat, 25 Oct 2025 11:28:04 -0500 Subject: [PATCH 1/7] Enhance authentication error handling in AuthForm and related components This commit introduces improved error handling for Google authentication in the AuthForm component. It adds a state to manage Google-specific error messages and integrates the getSignInErrorMessage function to provide user-friendly feedback based on authentication errors. Additionally, it refactors the form rendering logic to ensure that error messages are displayed appropriately across different authentication states, enhancing the overall user experience during sign-in and sign-up processes. --- .../components/main-components/AuthForm.tsx | 56 +++++++++++++++++-- app/(setup)/login/hooks/SignIn.ts | 7 +-- lib/auth/auth-error-messages.ts | 49 +++++++++++----- 3 files changed, 85 insertions(+), 27 deletions(-) diff --git a/app/(setup)/login/components/main-components/AuthForm.tsx b/app/(setup)/login/components/main-components/AuthForm.tsx index 40fde5e2..42a8776c 100644 --- a/app/(setup)/login/components/main-components/AuthForm.tsx +++ b/app/(setup)/login/components/main-components/AuthForm.tsx @@ -8,7 +8,8 @@ import { SignInForm } from '@/app/(setup)/login/components/sing-in/SignInForm'; import { SignUpForm } from '@/app/(setup)/login/components/sing-up/SignUpForm'; import { ForgotPasswordForm } from '@/app/(setup)/login/components/forgot-password/ForgotPasswordForm'; import { VerificationForm } from '@/app/(setup)/login/components/verification-form/VerificationForm'; -import { signInWithRedirect } from 'aws-amplify/auth'; +import { signInWithRedirect, AuthError } from 'aws-amplify/auth'; +import { getSignInErrorMessage } from '@/lib/auth/auth-error-messages'; type AuthState = 'signin' | 'signup' | 'forgot-password' | 'verification'; @@ -16,6 +17,11 @@ export function AuthForm() { const [authState, setAuthState] = useState('signin'); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); + const [googleError, setGoogleError] = useState(null); + + const clearGoogleError = useCallback(() => { + setGoogleError(null); + }, []); const renderForm = useCallback(() => { switch (authState) { @@ -24,11 +30,13 @@ export function AuthForm() { { setAuthState('forgot-password'); + clearGoogleError(); }} onVerificationNeeded={(email, password) => { setEmail(email); setPassword(password); setAuthState('verification'); + clearGoogleError(); }} /> ); @@ -39,15 +47,32 @@ export function AuthForm() { setEmail(email); setPassword(password); setAuthState('verification'); + clearGoogleError(); }} /> ); case 'forgot-password': - return setAuthState('signin')} />; + return ( + { + setAuthState('signin'); + clearGoogleError(); + }} + /> + ); case 'verification': - return setAuthState('signup')} />; + return ( + { + setAuthState('signup'); + clearGoogleError(); + }} + /> + ); } - }, [authState, email, password]); + }, [authState, email, password, clearGoogleError]); const handleLoginClick = async () => { try { @@ -56,6 +81,10 @@ export function AuthForm() { }); } catch (error) { console.error('Error during sign-in:', error); + + if (error instanceof AuthError) { + setGoogleError(getSignInErrorMessage(error)); + } } }; @@ -117,6 +146,9 @@ export function AuthForm() { + {/* Mostrar error de Google Auth */} + {googleError &&
{googleError}
} + {(authState === 'signin' || authState === 'signup') && ( <>
@@ -155,14 +187,26 @@ export function AuthForm() { {authState === 'signin' ? ( <> ¿No tienes una cuenta?{' '} - ) : ( <> ¿Ya tienes una cuenta?{' '} - diff --git a/app/(setup)/login/hooks/SignIn.ts b/app/(setup)/login/hooks/SignIn.ts index 8ac88ba7..7ab18e9f 100644 --- a/app/(setup)/login/hooks/SignIn.ts +++ b/app/(setup)/login/hooks/SignIn.ts @@ -1,4 +1,4 @@ -import { resendSignUpCode, signIn, type SignInInput } from 'aws-amplify/auth'; +import { resendSignUpCode, signIn, type SignInInput, AuthError } from 'aws-amplify/auth'; import { useRouter } from 'next/navigation'; import { useCallback, useState } from 'react'; import { useAuth } from '@/context/hooks/useAuth'; @@ -14,11 +14,6 @@ interface UseAuthReturn { resendConfirmationCode: (email: string) => Promise; } -interface AuthError { - code: string; - message: string; -} - interface UseAuthProps { redirectPath?: string; onVerificationNeeded?: (email: string, password: string) => void; diff --git a/lib/auth/auth-error-messages.ts b/lib/auth/auth-error-messages.ts index 4fe98c93..b5f483ca 100644 --- a/lib/auth/auth-error-messages.ts +++ b/lib/auth/auth-error-messages.ts @@ -2,18 +2,15 @@ * Mensajes de error centralizados para autenticación */ -export interface AuthError { - code?: string; - message?: string; -} +import { AuthError } from 'aws-amplify/auth'; +export type { AuthError }; /** * Obtiene el mensaje de error traducido para errores de registro */ export function getSignUpErrorMessage(error: AuthError): string { - // Manejo por código de error - if (error.code) { - switch (error.code) { + if (error.name) { + switch (error.name) { case 'UsernameExistsException': return 'Este correo electrónico ya está registrado'; case 'InvalidParameterException': @@ -27,7 +24,7 @@ export function getSignUpErrorMessage(error: AuthError): string { } } - // Manejo por mensaje de error + // Fallback al mensaje si no hay nombre específico switch (error.message) { case 'Username should be an email.': return 'El correo electrónico no tiene un formato válido'; @@ -44,7 +41,7 @@ export function getSignUpErrorMessage(error: AuthError): string { if (error.message?.includes('Exceeded daily email limit')) { return 'Se ha excedido el límite diario de envío de correos. Por favor, intenta mañana o contacta al soporte'; } - return 'Ha ocurrido un error. Por favor, intenta nuevamente'; + return error.recoverySuggestion || 'Ha ocurrido un error. Por favor, intenta nuevamente'; } } @@ -52,9 +49,9 @@ export function getSignUpErrorMessage(error: AuthError): string { * Obtiene el mensaje de error traducido para errores de confirmación de registro */ export function getConfirmSignUpErrorMessage(error: AuthError): string { - // Manejo por código de error - if (error.code) { - switch (error.code) { + // Usar el nombre del error (más confiable que el mensaje) + if (error.name) { + switch (error.name) { case 'CodeMismatchException': return 'El código ingresado no es válido. Por favor, verifica e intenta nuevamente'; case 'ExpiredCodeException': @@ -70,7 +67,7 @@ export function getConfirmSignUpErrorMessage(error: AuthError): string { } } - // Manejo por mensaje de error + // Fallback al mensaje si no hay nombre específico switch (error.message) { case 'Invalid verification code provided, please try again.': return 'Código de verificación inválido, por favor intenta nuevamente'; @@ -85,7 +82,7 @@ export function getConfirmSignUpErrorMessage(error: AuthError): string { if (error.message?.includes('Exceeded daily email limit')) { return 'Se ha excedido el límite diario de envío de correos. Por favor, intenta mañana o contacta al soporte'; } - return 'Ha ocurrido un error durante la verificación. Por favor, intenta nuevamente'; + return error.recoverySuggestion || 'Ha ocurrido un error durante la verificación. Por favor, intenta nuevamente'; } } @@ -93,6 +90,27 @@ export function getConfirmSignUpErrorMessage(error: AuthError): string { * Obtiene el mensaje de error traducido para errores de inicio de sesión */ export function getSignInErrorMessage(error: AuthError): string { + // Usar el nombre del error (más confiable que el mensaje) + if (error.name) { + switch (error.name) { + case 'NotAuthorizedException': + return 'Email o contraseña incorrectos'; + case 'UserNotConfirmedException': + return 'Por favor confirma tu cuenta primero'; + case 'TooManyRequestsException': + return 'Demasiados intentos. Por favor, intenta más tarde'; + case 'UserNotFoundException': + return 'No se encontró una cuenta con este correo electrónico'; + case 'InvalidParameterException': + return 'Uno o más campos contienen datos inválidos'; + case 'LimitExceededException': + return 'Se ha excedido el límite de intentos. Por favor, intenta más tarde'; + case 'NetworkError': + return 'Error de conexión. Por favor, verifica tu internet'; + } + } + + // Fallback al mensaje si no hay nombre específico switch (error.message) { case 'Incorrect username or password.': return 'Email o contraseña incorrectos'; @@ -107,6 +125,7 @@ export function getSignInErrorMessage(error: AuthError): string { case 'There is already a signed in user.': return 'Ya hay un usuario autenticado. Por favor, cierra la sesión primero'; default: - return error.message || 'Ha ocurrido un error durante el inicio de sesión'; + // Usar recoverySuggestion si está disponible, sino el mensaje por defecto + return error.recoverySuggestion || error.message || 'Ha ocurrido un error durante el inicio de sesión'; } } From 27c965fbe3be9bca127fd692c95f17faa165e031 Mon Sep 17 00:00:00 2001 From: Steven Date: Sun, 26 Oct 2025 18:52:26 -0500 Subject: [PATCH 2/7] Refactor StorePage component to use controllers and improve error handling This commit refactors the StorePage component by replacing direct logic with the StorePageController and StoreMetadataController for better separation of concerns. It removes unused imports and enhances error handling to render HTML error messages when available. Additionally, it updates the generateMetadata function to utilize the new controller, streamlining metadata generation. The DevAutoReloadScript and store-page-utils have been removed as they are no longer needed, simplifying the codebase. --- app/[store]/page.tsx | 86 +++----------- .../src/_lib/constants/store.constants.ts | 87 ++++++++++++++ .../controllers/store-metadata-controller.ts | 23 ++++ .../_lib/controllers/store-page-controller.ts | 110 ++++++++++++++++++ .../src/_lib/services/domain.service.ts | 30 +++++ .../_lib/services/store-tracking.service.ts | 25 ++++ .../components/DevAutoReloadScript.tsx | 0 app/[store]/{ => src}/lib/store-page-utils.ts | 34 ++---- .../components/NotificationPopover.tsx | 12 +- .../hooks/useNotificationPopover.ts | 2 +- .../components/SubscriptionSection.tsx | 2 +- .../notifications/useOrderNotifications.ts | 3 +- .../hooks/{ => utils}/useSubscriptionLogic.ts | 0 .../tags/filters/javascript-generator.ts | 2 +- .../liquid/tags/filters/js/filter-main.js | 18 +-- .../liquid/tags/filters/js/filter-state.js | 2 +- scripts/upload-base-template.js | 6 +- .../cache/cache-invalidation-service.test.ts | 7 +- test/unit/cache/cache-manager.test.ts | 2 +- .../liquid-filters/ecommerce-filters.test.ts | 2 +- test/unit/liquid-tags/render-tag.test.ts | 4 +- test/unit/liquid-tags/schema-tag.test.ts | 2 +- test/unit/liquid-tags/script-tag.test.ts | 2 +- test/unit/liquid-tags/setup.ts | 2 +- .../pipeline-steps/build-context-step.test.ts | 6 +- .../initialize-engine-step.test.ts | 6 +- .../pipeline-steps/load-data-step.test.ts | 10 +- .../render-content-step.test.ts | 6 +- .../pipeline-steps/resolve-store-step.test.ts | 6 +- 29 files changed, 352 insertions(+), 145 deletions(-) create mode 100644 app/[store]/src/_lib/constants/store.constants.ts create mode 100644 app/[store]/src/_lib/controllers/store-metadata-controller.ts create mode 100644 app/[store]/src/_lib/controllers/store-page-controller.ts create mode 100644 app/[store]/src/_lib/services/domain.service.ts create mode 100644 app/[store]/src/_lib/services/store-tracking.service.ts rename app/[store]/{ => src}/components/DevAutoReloadScript.tsx (100%) rename app/[store]/{ => src}/lib/store-page-utils.ts (84%) rename app/store/hooks/{ => utils}/useSubscriptionLogic.ts (100%) diff --git a/app/[store]/page.tsx b/app/[store]/page.tsx index 39384d61..4725c65c 100644 --- a/app/[store]/page.tsx +++ b/app/[store]/page.tsx @@ -1,20 +1,11 @@ -import DevAutoReloadScript from '@/app/[store]/components/DevAutoReloadScript'; -import { generateStoreMetadata, getCachedRenderResult, isAssetPath } from '@/app/[store]/lib/store-page-utils'; -import { logger } from '@fasttify/liquid-forge'; -import { storeViewsTracker } from '@fasttify/liquid-forge'; -import { domainResolver } from '@fasttify/liquid-forge'; +import DevAutoReloadScript from '@/app/[store]/src/components/DevAutoReloadScript'; +import { StorePageController } from '@/app/[store]/src/_lib/controllers/store-page-controller'; +import { StoreMetadataController } from '@/app/[store]/src/_lib/controllers/store-metadata-controller'; +import { isAssetPath } from '@/app/[store]/src/lib/store-page-utils'; import { Metadata } from 'next'; -import { headers } from 'next/headers'; -import { notFound } from 'next/navigation'; -import { Suspense } from 'react'; export const dynamic = 'force-dynamic'; -/** - * Función cacheada usando React.cache() que persiste entre generateMetadata y StorePage - * Esta es la forma oficial de Next.js para compartir datos entre estas funciones - */ - interface StorePageProps { params: Promise<{ store: string; @@ -28,54 +19,14 @@ interface StorePageProps { /** * Página principal de tienda con SSR * Maneja todas las rutas de tienda: /, /products/slug, /collections/slug, etc + * + * Esta página solo orquesta la llamada al controller, delegando toda la lógica */ -export default async function StorePage({ params, searchParams }: StorePageProps) { - const requestHeaders = await headers(); - const xOriginalHost = requestHeaders.get('x-original-host'); - - const hostname = - xOriginalHost || - (requestHeaders.get('cf-connecting-ip') - ? requestHeaders.get('x-forwarded-host') || requestHeaders.get('host') || '' - : requestHeaders.get('host') || ''); - - const cleanHostname = hostname?.split(':')[0] || ''; - - const isMainDomain = - cleanHostname === 'fasttify.com' || cleanHostname === 'www.fasttify.com' || cleanHostname === 'localhost'; - - if (isMainDomain) { - notFound(); - } - - const { store } = await params; - const awaitedSearchParams = await searchParams; - const path = awaitedSearchParams.path || '/'; - - if (isAssetPath(path)) { - notFound(); - } +export default async function StorePage(props: StorePageProps) { + const controller = new StorePageController(); try { - const domain = store.includes('.') ? store : `${store}.fasttify.com`; - - const result = await getCachedRenderResult(domain, path, awaitedSearchParams); - - // Trackear la vista de la tienda de forma asíncrona - try { - const headersObj = Object.fromEntries(requestHeaders.entries()); - const fullUrl = `https://${cleanHostname}${path}`; - - const storeRecord = await domainResolver.resolveStoreByDomain(domain); - const realStoreId = storeRecord.storeId; - - const viewData = storeViewsTracker.captureStoreView(realStoreId, path, headersObj, fullUrl); - storeViewsTracker.trackStoreView(viewData).catch((trackError) => { - logger.error(`[StorePage] Failed to track store view for ${realStoreId}`, trackError, 'StorePage'); - }); - } catch (trackError) { - logger.error(`[StorePage] Error capturing store view for ${domain}`, trackError, 'StorePage'); - } + const result = await controller.handle(props); return ( <> @@ -84,16 +35,12 @@ export default async function StorePage({ params, searchParams }: StorePageProps ); } catch (error: any) { - logger.error(`Error rendering store page ${store}${path}`, error, 'StorePage'); - + // Si hay HTML de error disponible, renderizarlo if (error.html) { return
; } - if (error.type === 'STORE_NOT_FOUND' && error.statusCode === 404) { - notFound(); - } - + // Re-lanzar otros errores throw error; } } @@ -101,17 +48,16 @@ export default async function StorePage({ params, searchParams }: StorePageProps /** * Genera metadata SEO para la página */ -export async function generateMetadata({ params, searchParams }: StorePageProps): Promise { - const { store } = await params; - const awaitedSearchParams = await searchParams; - const path = awaitedSearchParams.path || '/'; +export async function generateMetadata(props: StorePageProps): Promise { + const { path } = await props.searchParams; - if (isAssetPath(path)) { + if (path && isAssetPath(path)) { return { title: 'Asset', description: 'Static asset', }; } - return generateStoreMetadata(store, path, awaitedSearchParams); + const controller = new StoreMetadataController(); + return controller.handle(props); } diff --git a/app/[store]/src/_lib/constants/store.constants.ts b/app/[store]/src/_lib/constants/store.constants.ts new file mode 100644 index 00000000..fbac1bac --- /dev/null +++ b/app/[store]/src/_lib/constants/store.constants.ts @@ -0,0 +1,87 @@ +/** + * Constantes para el módulo de tiendas + */ + +/** + * Configuración de dominios + */ +export const DOMAIN_CONFIG = { + BASE_DOMAIN: 'fasttify.com', + WWW_DOMAIN: 'www.fasttify.com', + LOCALHOST: 'localhost', +} as const; + +/** + * Header names para extracción de hostname + */ +export const HEADER_NAMES = { + X_ORIGINAL_HOST: 'x-original-host', + X_FORWARDED_HOST: 'x-forwarded-host', + HOST: 'host', + CF_CONNECTING_IP: 'cf-connecting-ip', +} as const; + +/** + * Protocolos y separadores + */ +export const URL_CONFIG = { + PROTOCOL: 'https://', + PORT_SEPARATOR: ':', +} as const; + +/** + * Tipos de errores + */ +export const ERROR_TYPES = { + STORE_NOT_FOUND: 'STORE_NOT_FOUND', +} as const; + +/** + * Códigos de estado HTTP + */ +export const HTTP_STATUS = { + NOT_FOUND: 404, +} as const; + +/** + * Assets comunes que los navegadores solicitan automáticamente + */ +export const COMMON_ASSETS = [ + 'favicon.ico', + 'robots.txt', + 'sitemap.xml', + 'apple-touch-icon.png', + 'manifest.json', +] as const; + +/** + * Extensiones de archivos consideradas como assets estáticos + */ +export const ASSET_EXTENSIONS = [ + '.png', + '.jpg', + '.jpeg', + '.gif', + '.svg', + '.webp', + '.ico', + '.css', + '.js', + '.woff', + '.woff2', + '.ttf', + '.eot', + '.otf', + '.json', + '.xml', + '.txt', +] as const; + +/** + * Patrones de paths que indican assets + */ +export const ASSET_PATH_PATTERNS = { + ASSETS_FOLDER: '/assets/', + NEXT_FOLDER: '/_next/', + ICONS_FOLDER: '/icons/', +} as const; diff --git a/app/[store]/src/_lib/controllers/store-metadata-controller.ts b/app/[store]/src/_lib/controllers/store-metadata-controller.ts new file mode 100644 index 00000000..9919d319 --- /dev/null +++ b/app/[store]/src/_lib/controllers/store-metadata-controller.ts @@ -0,0 +1,23 @@ +import { Metadata } from 'next'; +import { generateStoreMetadata as generateMetadataUtil } from '@/app/[store]/src/lib/store-page-utils'; + +interface StoreMetadataProps { + params: Promise<{ store: string }>; + searchParams: Promise<{ path?: string; [key: string]: string | string[] | undefined }>; +} + +/** + * Controlador para generar metadata SEO de las páginas de tiendas + */ +export class StoreMetadataController { + /** + * Genera metadata SEO para la página + */ + async handle(props: StoreMetadataProps): Promise { + const { store } = await props.params; + const awaitedSearchParams = await props.searchParams; + const path = awaitedSearchParams.path || '/'; + + return generateMetadataUtil(store, path, awaitedSearchParams); + } +} diff --git a/app/[store]/src/_lib/controllers/store-page-controller.ts b/app/[store]/src/_lib/controllers/store-page-controller.ts new file mode 100644 index 00000000..14cd3d97 --- /dev/null +++ b/app/[store]/src/_lib/controllers/store-page-controller.ts @@ -0,0 +1,110 @@ +import { logger } from '@fasttify/liquid-forge'; +import { headers } from 'next/headers'; +import { notFound } from 'next/navigation'; +import { DomainService } from '@/app/[store]/src/_lib/services/domain.service'; +import { StoreTrackingService } from '@/app/[store]/src/_lib/services/store-tracking.service'; +import { getCachedRenderResult, isAssetPath } from '@/app/[store]/src/lib/store-page-utils'; +import { HEADER_NAMES, URL_CONFIG, ERROR_TYPES, HTTP_STATUS } from '@/app/[store]/src/_lib/constants/store.constants'; + +interface StorePageProps { + params: Promise<{ store: string }>; + searchParams: Promise<{ path?: string; [key: string]: string | string[] | undefined }>; +} + +/** + * Controlador para manejar el renderizado de páginas de tiendas + * Contiene toda la lógica de negocio, dejando page.tsx limpio + */ +export class StorePageController { + private domainService: DomainService; + private storeTrackingService: StoreTrackingService; + + constructor() { + this.domainService = new DomainService(); + this.storeTrackingService = new StoreTrackingService(); + } + + /** + * Maneja la petición de renderizado de una página de tienda + */ + async handle(props: StorePageProps) { + const requestHeaders = await headers(); + + // Obtener hostname + const hostname = this.getHostname(requestHeaders); + const { PORT_SEPARATOR } = URL_CONFIG; + const cleanHostname = hostname?.split(PORT_SEPARATOR)[0] || ''; + + // Validar dominio principal + if (this.domainService.isMainDomain(cleanHostname)) { + notFound(); + } + + const { store } = await props.params; + const awaitedSearchParams = await props.searchParams; + const path = awaitedSearchParams.path || '/'; + + // Validar si el parámetro store es un asset común (favicon.ico, robots.txt, etc.) + if (this.domainService.isCommonAsset(store)) { + notFound(); + } + + // Validar path de assets + if (isAssetPath(path)) { + notFound(); + } + + // Resolver dominio + const domain = this.domainService.resolveDomainFromParam(store); + + try { + // Renderizar página + const result = await getCachedRenderResult(domain, path, awaitedSearchParams); + + // Trackear vista de forma asíncrona + await this.trackStoreView(domain, path, cleanHostname, requestHeaders); + + return { html: result.html }; + } catch (error: any) { + // Si es 404 de tienda no encontrada, lanzar notFound + if (error.type === ERROR_TYPES.STORE_NOT_FOUND && error.statusCode === HTTP_STATUS.NOT_FOUND) { + notFound(); + } + + // Re-lanzar el error para que page.tsx lo maneje + throw error; + } + } + + /** + * Obtiene el hostname desde los headers + */ + private getHostname(requestHeaders: Headers): string { + const { X_ORIGINAL_HOST, CF_CONNECTING_IP, X_FORWARDED_HOST, HOST } = HEADER_NAMES; + + const xOriginalHost = requestHeaders.get(X_ORIGINAL_HOST); + + return ( + xOriginalHost || + (requestHeaders.get(CF_CONNECTING_IP) + ? requestHeaders.get(X_FORWARDED_HOST) || requestHeaders.get(HOST) || '' + : requestHeaders.get(HOST) || '') + ); + } + + /** + * Trackea la vista de la tienda + */ + private async trackStoreView(domain: string, path: string, hostname: string, requestHeaders: Headers): Promise { + try { + const { PROTOCOL } = URL_CONFIG; + const headersObj = Object.fromEntries(requestHeaders.entries()); + const fullUrl = `${PROTOCOL}${hostname}${path}`; + + await this.storeTrackingService.trackStoreView(domain, path, headersObj, fullUrl); + } catch (error) { + // El tracking falla silenciosamente para no afectar el renderizado + logger.error(`[StorePageController] Error in tracking for ${domain}`, error, 'StorePageController'); + } + } +} diff --git a/app/[store]/src/_lib/services/domain.service.ts b/app/[store]/src/_lib/services/domain.service.ts new file mode 100644 index 00000000..72a3135e --- /dev/null +++ b/app/[store]/src/_lib/services/domain.service.ts @@ -0,0 +1,30 @@ +import { DOMAIN_CONFIG, COMMON_ASSETS } from '@/app/[store]/src/_lib/constants/store.constants'; + +/** + * Servicio para manejar la lógica de dominios + */ +export class DomainService { + /** + * Resuelve el dominio de la tienda a partir del parámetro store + */ + resolveDomainFromParam(store: string): string { + const { BASE_DOMAIN } = DOMAIN_CONFIG; + return store.includes('.') ? store : `${store}.${BASE_DOMAIN}`; + } + + /** + * Verifica si el dominio es el dominio principal (no debe ser renderizado) + */ + isMainDomain(hostname: string): boolean { + const { BASE_DOMAIN, WWW_DOMAIN, LOCALHOST } = DOMAIN_CONFIG; + return hostname === BASE_DOMAIN || hostname === WWW_DOMAIN || hostname === LOCALHOST; + } + + /** + * Verifica si el parámetro store es un asset común (favicon.ico, robots.txt, etc.) + * Estos son solicitados automáticamente por navegadores y no deberían procesarse como tiendas + */ + isCommonAsset(store: string): boolean { + return COMMON_ASSETS.includes(store as any); + } +} diff --git a/app/[store]/src/_lib/services/store-tracking.service.ts b/app/[store]/src/_lib/services/store-tracking.service.ts new file mode 100644 index 00000000..9f3916cb --- /dev/null +++ b/app/[store]/src/_lib/services/store-tracking.service.ts @@ -0,0 +1,25 @@ +import { storeViewsTracker, logger, domainResolver } from '@fasttify/liquid-forge'; + +/** + * Servicio para manejar el tracking de vistas de tiendas + */ +export class StoreTrackingService { + /** + * Captura y registra una vista de la tienda de forma asíncrona + */ + async trackStoreView(domain: string, path: string, headers: Record, fullUrl: string): Promise { + try { + const storeRecord = await domainResolver.resolveStoreByDomain(domain); + const realStoreId = storeRecord.storeId; + + const viewData = storeViewsTracker.captureStoreView(realStoreId, path, headers, fullUrl); + + // Trackear de forma asíncrona (fire and forget) + storeViewsTracker.trackStoreView(viewData).catch((trackError) => { + logger.error(`Failed to track store view for ${realStoreId}`, trackError, 'StoreTrackingService'); + }); + } catch (trackError) { + logger.error(`Error capturing store view for ${domain}`, trackError, 'StoreTrackingService'); + } + } +} diff --git a/app/[store]/components/DevAutoReloadScript.tsx b/app/[store]/src/components/DevAutoReloadScript.tsx similarity index 100% rename from app/[store]/components/DevAutoReloadScript.tsx rename to app/[store]/src/components/DevAutoReloadScript.tsx diff --git a/app/[store]/lib/store-page-utils.ts b/app/[store]/src/lib/store-page-utils.ts similarity index 84% rename from app/[store]/lib/store-page-utils.ts rename to app/[store]/src/lib/store-page-utils.ts index d6e5901d..33a85086 100644 --- a/app/[store]/lib/store-page-utils.ts +++ b/app/[store]/src/lib/store-page-utils.ts @@ -1,34 +1,21 @@ import { storeRenderer, logger } from '@fasttify/liquid-forge'; import { Metadata } from 'next'; import { cache } from 'react'; +import { ASSET_EXTENSIONS, ASSET_PATH_PATTERNS, DOMAIN_CONFIG } from '@/app/[store]/src/_lib/constants/store.constants'; /** * Verifica si el path corresponde a un asset estático para evitar procesarlo como una página de tienda. */ export function isAssetPath(path: string): boolean { - const assetExtensions = [ - '.png', - '.jpg', - '.jpeg', - '.gif', - '.svg', - '.webp', - '.ico', - '.css', - '.js', - '.woff', - '.woff2', - '.ttf', - '.eot', - ]; - return ( - assetExtensions.some((ext) => path.toLowerCase().endsWith(ext)) || - path.startsWith('/assets/') || - path.startsWith('/_next/') || - path.includes('/icons/') - ); -} + const { ASSETS_FOLDER, NEXT_FOLDER, ICONS_FOLDER } = ASSET_PATH_PATTERNS; + + const hasAssetExtension = ASSET_EXTENSIONS.some((ext) => path.toLowerCase().endsWith(ext)); + const matchesPathPattern = + path.startsWith(ASSETS_FOLDER) || path.startsWith(NEXT_FOLDER) || path.includes(ICONS_FOLDER); + + return hasAssetExtension || matchesPathPattern; +} /** * Función cacheada usando React.cache() que persiste entre generateMetadata y StorePage. * Esta es la forma oficial de Next.js para compartir datos entre estas funciones. @@ -47,7 +34,8 @@ export async function generateStoreMetadata( searchParams: Record ): Promise { try { - const domain = store.includes('.') ? store : `${store}.fasttify.com`; + const { BASE_DOMAIN } = DOMAIN_CONFIG; + const domain = store.includes('.') ? store : `${store}.${BASE_DOMAIN}`; const result = await getCachedRenderResult(domain, path, searchParams); const { metadata } = result; diff --git a/app/store/components/notifications/components/NotificationPopover.tsx b/app/store/components/notifications/components/NotificationPopover.tsx index 7e38430e..cb0302ef 100644 --- a/app/store/components/notifications/components/NotificationPopover.tsx +++ b/app/store/components/notifications/components/NotificationPopover.tsx @@ -1,12 +1,12 @@ import { memo, useRef, useMemo } from 'react'; import { Popover, Badge, Spinner, TopBar, Icon } from '@shopify/polaris'; -import { NotificationHeader } from './NotificationHeader'; -import { NotificationList } from './NotificationList'; -import { NotificationFooter } from './NotificationFooter'; -import { NotificationEmptyState } from './NotificationEmptyState'; +import { NotificationHeader } from '@/app/store/components/notifications/components/NotificationHeader'; +import { NotificationList } from '@/app/store/components/notifications/components/NotificationList'; +import { NotificationFooter } from '@/app/store/components/notifications/components/NotificationFooter'; +import { NotificationEmptyState } from '@/app/store/components/notifications/components/NotificationEmptyState'; import { NotificationIcon } from '@shopify/polaris-icons'; -import { useNotificationPopover } from '../hooks'; -import { formatNotificationTime } from '../utils/formatNotificationTime'; +import { useNotificationPopover } from '@/app/store/components/notifications/hooks'; +import { formatNotificationTime } from '@/app/store/components/notifications/utils/formatNotificationTime'; interface NotificationPopoverProps { storeId?: string; diff --git a/app/store/components/notifications/hooks/useNotificationPopover.ts b/app/store/components/notifications/hooks/useNotificationPopover.ts index 56a8467f..6fd6e14b 100644 --- a/app/store/components/notifications/hooks/useNotificationPopover.ts +++ b/app/store/components/notifications/hooks/useNotificationPopover.ts @@ -1,7 +1,7 @@ import { useState, useCallback, useEffect, useRef } from 'react'; import { useNotifications } from '@/app/store/hooks/data/useNotifications'; import useStoreDataStore from '@/context/core/storeDataStore'; -import { useNotificationSound } from './useNotificationSound'; +import { useNotificationSound } from '@/app/store/components/notifications/hooks/useNotificationSound'; interface UseNotificationPopoverProps { storeId?: string; diff --git a/app/store/components/profile/components/SubscriptionSection.tsx b/app/store/components/profile/components/SubscriptionSection.tsx index 01723bf4..926cfb7b 100644 --- a/app/store/components/profile/components/SubscriptionSection.tsx +++ b/app/store/components/profile/components/SubscriptionSection.tsx @@ -3,7 +3,7 @@ import { ExternalIcon, CheckCircleIcon } from '@shopify/polaris-icons'; import { useState, memo } from 'react'; import { plans } from '@/app/(www)/pricing/components/plans'; import type { UserProps } from '@/app/store/components/profile/types'; -import { useSubscriptionLogic } from '@/app/store/hooks/useSubscriptionLogic'; +import { useSubscriptionLogic } from '@/app/store/hooks/utils/useSubscriptionLogic'; interface SubscriptionSectionProps extends UserProps { storeId: string; diff --git a/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts b/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts index 46373cb5..6e495a14 100644 --- a/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts +++ b/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts @@ -1,8 +1,7 @@ import { useCallback } from 'react'; import { useEmailNotifications } from '@/app/store/hooks/api/useEmailNotifications'; import type { OrderStatus, PaymentStatus } from '@/app/store/hooks/data/useOrders/types'; -import { EmailFormattingUtils } from '@/packages/liquid-forge/services/notifications/client-utils'; -import { getOrderStatus, getPaymentStatus } from '@/packages/liquid-forge/services/notifications/status-translations'; +import { EmailFormattingUtils, getOrderStatus, getPaymentStatus } from '@fasttify/liquid-forge/services/notifications'; /** * Hook para manejar las notificaciones de email relacionadas con órdenes diff --git a/app/store/hooks/useSubscriptionLogic.ts b/app/store/hooks/utils/useSubscriptionLogic.ts similarity index 100% rename from app/store/hooks/useSubscriptionLogic.ts rename to app/store/hooks/utils/useSubscriptionLogic.ts diff --git a/packages/liquid-forge/liquid/tags/filters/javascript-generator.ts b/packages/liquid-forge/liquid/tags/filters/javascript-generator.ts index 5d4e405c..722334b2 100644 --- a/packages/liquid-forge/liquid/tags/filters/javascript-generator.ts +++ b/packages/liquid-forge/liquid/tags/filters/javascript-generator.ts @@ -65,7 +65,7 @@ export class FilterJavaScriptGenerator { */ private static buildScriptContent(filterConfig: string): string { return ` - import { initFilterSystem } from 'https://cdn.fasttify.com/assets/filter-main.js'; + import { initFilterSystem } from 'https://cdn.fasttify.com/assets/global-static/filter-main.js'; const FILTER_CONFIG = ${filterConfig}; initFilterSystem(FILTER_CONFIG); `; diff --git a/packages/liquid-forge/liquid/tags/filters/js/filter-main.js b/packages/liquid-forge/liquid/tags/filters/js/filter-main.js index 3a354a66..65bf5f71 100644 --- a/packages/liquid-forge/liquid/tags/filters/js/filter-main.js +++ b/packages/liquid-forge/liquid/tags/filters/js/filter-main.js @@ -17,15 +17,15 @@ /** * Sistema principal de filtros */ -import { DOMManager } from 'https://cdn.fasttify.com/assets/dom-manager.js'; -import { EventHandler } from 'https://cdn.fasttify.com/assets/event-handler.js'; -import { FilterAPI } from 'https://cdn.fasttify.com/assets/filter-api.js'; -import { createFilterConfig } from 'https://cdn.fasttify.com/assets/filter-config.js'; -import { FilterStateManager } from 'https://cdn.fasttify.com/assets/filter-state.js'; -import { PriceSlider } from 'https://cdn.fasttify.com/assets/price-slider.js'; -import { ProductRenderer } from 'https://cdn.fasttify.com/assets/product-renderer.js'; -import { URLLoader } from 'https://cdn.fasttify.com/assets/url-loader.js'; -import { FilterUtils } from 'https://cdn.fasttify.com/assets/utils.js'; +import { DOMManager } from 'https://cdn.fasttify.com/assets/global-static/dom-manager.js'; +import { EventHandler } from 'https://cdn.fasttify.com/assets/global-static/event-handler.js'; +import { FilterAPI } from 'https://cdn.fasttify.com/assets/global-static/filter-api.js'; +import { createFilterConfig } from 'https://cdn.fasttify.com/assets/global-static/filter-config.js'; +import { FilterStateManager } from 'https://cdn.fasttify.com/assets/global-static/filter-state.js'; +import { PriceSlider } from 'https://cdn.fasttify.com/assets/global-static/price-slider.js'; +import { ProductRenderer } from 'https://cdn.fasttify.com/assets/global-static/product-renderer.js'; +import { URLLoader } from 'https://cdn.fasttify.com/assets/global-static/url-loader.js'; +import { FilterUtils } from 'https://cdn.fasttify.com/assets/global-static/utils.js'; /** * Clase principal del sistema de filtros diff --git a/packages/liquid-forge/liquid/tags/filters/js/filter-state.js b/packages/liquid-forge/liquid/tags/filters/js/filter-state.js index b846ccdf..0d475bfd 100644 --- a/packages/liquid-forge/liquid/tags/filters/js/filter-state.js +++ b/packages/liquid-forge/liquid/tags/filters/js/filter-state.js @@ -14,7 +14,7 @@ * limitations under the License. */ -import { INITIAL_FILTER_STATE } from 'https://cdn.fasttify.com/assets/filter-config.js'; +import { INITIAL_FILTER_STATE } from 'https://cdn.fasttify.com/assets/global-static/filter-config.js'; /** * Gestor del estado del filtro diff --git a/scripts/upload-base-template.js b/scripts/upload-base-template.js index 74b59d2f..0cec2ecf 100644 --- a/scripts/upload-base-template.js +++ b/scripts/upload-base-template.js @@ -24,7 +24,7 @@ const CLOUDFRONT_DISTRIBUTION_ID = process.env.CLOUDFRONT_DISTRIBUTION_ID; const BASE_TEMPLATE_PREFIX = 'base-templates/default/'; const TEMPLATE_DIR = join(process.cwd(), 'packages/example-themes/fasttify/theme'); const FILTER_MODULES_DIR = join(process.cwd(), 'packages/liquid-forge/liquid/tags/filters/js'); -const FILTER_MODULES_PREFIX = 'assets/'; +const FILTER_MODULES_PREFIX = 'assets/global-static/'; const s3Client = new S3Client({ region: REGION, @@ -231,7 +231,7 @@ async function invalidateCloudFrontCache() { InvalidationBatch: { Paths: { Quantity: 1, - Items: ['/assets/*'], + Items: ['/assets/global-static/*'], }, CallerReference: `invalidation-${Date.now()}`, }, @@ -271,7 +271,7 @@ async function main() { console.log('\nSubida completada con éxito!'); console.log(`Se subieron ${templateResults.length} archivos de plantilla.`); console.log(`Se subieron ${moduleResults.length} módulos de filtros.`); - console.log(`\nLos módulos están disponibles en: https://cdn.fasttify.com/assets/`); + console.log(`\nLos módulos están disponibles en: https://cdn.fasttify.com/assets/global-static/`); console.log('\nInvalidando cache de CloudFront...'); await invalidateCloudFrontCache(); diff --git a/test/unit/cache/cache-invalidation-service.test.ts b/test/unit/cache/cache-invalidation-service.test.ts index b65bdefd..2e670e39 100644 --- a/test/unit/cache/cache-invalidation-service.test.ts +++ b/test/unit/cache/cache-invalidation-service.test.ts @@ -1,5 +1,5 @@ -jest.mock('@/packages/liquid-forge/services/core/cache/cache-manager', () => { - const original = jest.requireActual('@/packages/liquid-forge/services/core/cache/cache-manager'); +jest.mock('@fasttify/liquid-forge/services/core/cache/cache-manager', () => { + const original = jest.requireActual('@fasttify/liquid-forge/services/core/cache/cache-manager'); return { ...original, cacheManager: { @@ -11,8 +11,7 @@ jest.mock('@/packages/liquid-forge/services/core/cache/cache-manager', () => { }; }); -import { cacheInvalidationService } from '@/packages/liquid-forge/services/core/cache/cache-invalidation-service'; -import { cacheManager } from '@/packages/liquid-forge/services/core/cache'; +import { cacheInvalidationService, cacheManager } from '@fasttify/liquid-forge/services/core/cache'; describe('CacheInvalidationService', () => { beforeEach(() => { diff --git a/test/unit/cache/cache-manager.test.ts b/test/unit/cache/cache-manager.test.ts index 4466787e..c1ea61ae 100644 --- a/test/unit/cache/cache-manager.test.ts +++ b/test/unit/cache/cache-manager.test.ts @@ -1,4 +1,4 @@ -import { cacheManager } from '@/packages/liquid-forge/services/core/cache'; +import { cacheManager } from '@fasttify/liquid-forge/services/core/cache'; describe('CacheManager (node-cache integration)', () => { beforeEach(() => { diff --git a/test/unit/liquid-filters/ecommerce-filters.test.ts b/test/unit/liquid-filters/ecommerce-filters.test.ts index 702c5224..ce6c403e 100644 --- a/test/unit/liquid-filters/ecommerce-filters.test.ts +++ b/test/unit/liquid-filters/ecommerce-filters.test.ts @@ -1,5 +1,5 @@ import { Liquid } from 'liquidjs'; -import { ecommerceFilters } from '@/packages/liquid-forge/liquid/filters/ecommerce-filters'; +import { ecommerceFilters } from '@fasttify/liquid-forge/liquid/filters/ecommerce-filters'; describe('Ecommerce Filters - Image Optimization', () => { let liquid: Liquid; diff --git a/test/unit/liquid-tags/render-tag.test.ts b/test/unit/liquid-tags/render-tag.test.ts index c2d5ffa6..ff86e4e7 100644 --- a/test/unit/liquid-tags/render-tag.test.ts +++ b/test/unit/liquid-tags/render-tag.test.ts @@ -1,10 +1,10 @@ -import { RenderTag } from '@/packages/liquid-forge/liquid/tags/core/render-tag'; +import { RenderTag } from '@fasttify/liquid-forge/liquid/tags/core/render-tag'; import { beforeEach, describe, expect, it, jest } from '@jest/globals'; import { Liquid } from 'liquidjs'; import { createTestContext, createTestLiquid, mockTemplateLoader } from './setup'; // Mock del TemplateLoader -jest.mock('@/packages/liquid-forge/services/templates/template-loader', () => ({ +jest.mock('@fasttify/liquid-forge/services/templates/template-loader', () => ({ TemplateLoader: { getInstance: () => mockTemplateLoader, }, diff --git a/test/unit/liquid-tags/schema-tag.test.ts b/test/unit/liquid-tags/schema-tag.test.ts index 457b01a0..283e7944 100644 --- a/test/unit/liquid-tags/schema-tag.test.ts +++ b/test/unit/liquid-tags/schema-tag.test.ts @@ -1,4 +1,4 @@ -import { SchemaTag } from '@/packages/liquid-forge/liquid/tags/data/schema-tag'; +import { SchemaTag } from '@fasttify/liquid-forge/liquid/tags/data/schema-tag'; import { beforeEach, describe, expect, it } from '@jest/globals'; import { Liquid } from 'liquidjs'; import { createTestContext, createTestLiquid } from './setup'; diff --git a/test/unit/liquid-tags/script-tag.test.ts b/test/unit/liquid-tags/script-tag.test.ts index 08c9872a..a79b5f6d 100644 --- a/test/unit/liquid-tags/script-tag.test.ts +++ b/test/unit/liquid-tags/script-tag.test.ts @@ -1,4 +1,4 @@ -import { ScriptTag } from '@/packages/liquid-forge/liquid/tags/styling/script-tag'; +import { ScriptTag } from '@fasttify/liquid-forge/liquid/tags/styling/script-tag'; import { beforeEach, describe, expect, it } from '@jest/globals'; import { Liquid } from 'liquidjs'; import { createTestContext, createTestLiquid } from './setup'; diff --git a/test/unit/liquid-tags/setup.ts b/test/unit/liquid-tags/setup.ts index 25580a27..3dd2f7d4 100644 --- a/test/unit/liquid-tags/setup.ts +++ b/test/unit/liquid-tags/setup.ts @@ -1,5 +1,5 @@ import { Liquid } from 'liquidjs'; -import type { RenderContext } from '@/packages/liquid-forge/types/template'; +import type { RenderContext } from '@fasttify/liquid-forge/types/template'; // Crear contexto de prueba simple export const createTestContext = (customData: Partial = {}): RenderContext => { diff --git a/test/unit/pipeline-steps/build-context-step.test.ts b/test/unit/pipeline-steps/build-context-step.test.ts index d007287a..eb5fa06d 100644 --- a/test/unit/pipeline-steps/build-context-step.test.ts +++ b/test/unit/pipeline-steps/build-context-step.test.ts @@ -1,11 +1,11 @@ -jest.mock('@/packages/liquid-forge/services/rendering/global-context', () => ({ +jest.mock('@fasttify/liquid-forge/services/rendering/global-context', () => ({ contextBuilder: { createRenderContext: jest.fn().mockResolvedValue({ context: 'ok' }), }, })); -import { buildContextStep } from '@/packages/liquid-forge/renderers/pipeline-steps/build-context-step'; -import { contextBuilder } from '@/packages/liquid-forge/services/rendering/global-context'; +import { buildContextStep } from '@fasttify/liquid-forge/renderers/pipeline-steps/build-context-step'; +import { contextBuilder } from '@fasttify/liquid-forge/services/rendering/global-context'; describe('buildContextStep', () => { it('debe construir el contexto correctamente', async () => { diff --git a/test/unit/pipeline-steps/initialize-engine-step.test.ts b/test/unit/pipeline-steps/initialize-engine-step.test.ts index f37f8bb1..d0653a72 100644 --- a/test/unit/pipeline-steps/initialize-engine-step.test.ts +++ b/test/unit/pipeline-steps/initialize-engine-step.test.ts @@ -1,5 +1,5 @@ // Mock para liquidEngine y assetCollector -jest.mock('@/packages/liquid-forge/liquid/engine', () => ({ +jest.mock('@fasttify/liquid-forge/liquid/engine', () => ({ liquidEngine: { assetCollector: { clear: jest.fn(), @@ -7,8 +7,8 @@ jest.mock('@/packages/liquid-forge/liquid/engine', () => ({ }, })); -import { liquidEngine } from '@/packages/liquid-forge/liquid/engine'; -import { initializeEngineStep } from '@/packages/liquid-forge/renderers/pipeline-steps/initialize-engine-step'; +import { liquidEngine } from '@fasttify/liquid-forge/liquid/engine'; +import { initializeEngineStep } from '@fasttify/liquid-forge/renderers/pipeline-steps/initialize-engine-step'; describe('initializeEngineStep', () => { it('debe limpiar el assetCollector y retornar los datos sin modificar', async () => { diff --git a/test/unit/pipeline-steps/load-data-step.test.ts b/test/unit/pipeline-steps/load-data-step.test.ts index 76c590eb..9ac1ce31 100644 --- a/test/unit/pipeline-steps/load-data-step.test.ts +++ b/test/unit/pipeline-steps/load-data-step.test.ts @@ -1,5 +1,5 @@ // Mocks para dependencias -jest.mock('@/packages/liquid-forge/services/templates/template-loader', () => ({ +jest.mock('@fasttify/liquid-forge/services/templates/template-loader', () => ({ templateLoader: { loadMainLayout: jest.fn().mockResolvedValue('layout-content'), loadMainLayoutCompiled: jest.fn().mockResolvedValue(['compiled-layout']), @@ -7,25 +7,25 @@ jest.mock('@/packages/liquid-forge/services/templates/template-loader', () => ({ loadCompiledTemplate: jest.fn().mockResolvedValue(['compiled-page-template']), }, })); -jest.mock('@/packages/liquid-forge/services/page/dynamic-data-loader', () => ({ +jest.mock('@fasttify/liquid-forge/services/page/dynamic-data-loader', () => ({ dynamicDataLoader: { loadDynamicData: jest .fn() .mockResolvedValue({ analysis: { requiredData: new Map(), liquidObjects: [], dependencies: [] } }), }, })); -jest.mock('@/packages/liquid-forge/services/fetchers/data-fetcher', () => ({ +jest.mock('@fasttify/liquid-forge/services/fetchers/data-fetcher', () => ({ dataFetcher: { getStoreNavigationMenus: jest.fn().mockResolvedValue('store-template'), }, })); -jest.mock('@/packages/liquid-forge/config/page-config', () => ({ +jest.mock('@fasttify/liquid-forge/config/page-config', () => ({ pageConfig: { getTemplatePath: jest.fn().mockReturnValue('page.liquid'), }, })); -import { loadDataStep } from '@/packages/liquid-forge/renderers/pipeline-steps/load-data-step'; +import { loadDataStep } from '@fasttify/liquid-forge/renderers/pipeline-steps/load-data-step'; describe('loadDataStep', () => { it('debe cargar layout, compilados, datos dinámicos y template en paralelo', async () => { diff --git a/test/unit/pipeline-steps/render-content-step.test.ts b/test/unit/pipeline-steps/render-content-step.test.ts index b8bb686d..125a6d5e 100644 --- a/test/unit/pipeline-steps/render-content-step.test.ts +++ b/test/unit/pipeline-steps/render-content-step.test.ts @@ -1,9 +1,9 @@ -jest.mock('@/packages/liquid-forge/services/rendering/render-page-content', () => ({ +jest.mock('@fasttify/liquid-forge/services/rendering/render-page-content', () => ({ renderPageContent: jest.fn().mockResolvedValue('contenido-renderizado'), })); -import { renderContentStep } from '@/packages/liquid-forge/renderers/pipeline-steps/render-content-step'; -import { renderPageContent } from '@/packages/liquid-forge/services/rendering/render-page-content'; +import { renderContentStep } from '@fasttify/liquid-forge/renderers/pipeline-steps/render-content-step'; +import { renderPageContent } from '@fasttify/liquid-forge/services/rendering/render-page-content'; describe('renderContentStep', () => { it('debe renderizar el contenido de la página y asignarlo a renderedContent', async () => { diff --git a/test/unit/pipeline-steps/resolve-store-step.test.ts b/test/unit/pipeline-steps/resolve-store-step.test.ts index e1461d7d..f121ad64 100644 --- a/test/unit/pipeline-steps/resolve-store-step.test.ts +++ b/test/unit/pipeline-steps/resolve-store-step.test.ts @@ -8,14 +8,14 @@ jest.mock('aws-amplify/auth/server', () => ({ getCurrentUser: jest.fn(), })); -jest.mock('@/packages/liquid-forge/services/core/domain-resolver', () => ({ +jest.mock('@fasttify/liquid-forge/services/core/domain-resolver', () => ({ domainResolver: { resolveStoreByDomain: jest.fn(), }, })); -import { resolveStoreStep } from '@/packages/liquid-forge/renderers/pipeline-steps/resolve-store-step'; -import { domainResolver } from '@/packages/liquid-forge/services/core/domain-resolver'; +import { resolveStoreStep } from '@fasttify/liquid-forge/renderers/pipeline-steps/resolve-store-step'; +import { domainResolver } from '@fasttify/liquid-forge/services/core/domain-resolver'; describe('resolveStoreStep', () => { it('debe resolver la tienda correctamente', async () => { From 71ee457f91d657db086e1c7c166d879018cbdc22 Mon Sep 17 00:00:00 2001 From: Steven Date: Sun, 26 Oct 2025 19:01:22 -0500 Subject: [PATCH 3/7] Refactor order notifications hook to improve import structure This commit refactors the useOrderNotifications hook by updating the import paths for getOrderStatus and getPaymentStatus functions, enhancing clarity and maintainability. The changes streamline the notification handling logic related to orders, ensuring better organization of the codebase. --- .../data/useOrders/notifications/useOrderNotifications.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts b/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts index 6e495a14..7007dc81 100644 --- a/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts +++ b/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts @@ -1,7 +1,8 @@ import { useCallback } from 'react'; import { useEmailNotifications } from '@/app/store/hooks/api/useEmailNotifications'; import type { OrderStatus, PaymentStatus } from '@/app/store/hooks/data/useOrders/types'; -import { EmailFormattingUtils, getOrderStatus, getPaymentStatus } from '@fasttify/liquid-forge/services/notifications'; +import { EmailFormattingUtils } from '@fasttify/liquid-forge/services/notifications'; +import { getOrderStatus, getPaymentStatus } from '@fasttify/liquid-forge/services/notifications/status-translations'; /** * Hook para manejar las notificaciones de email relacionadas con órdenes From d5c60631247ee9a83cec76d8b0f561931b97bda4 Mon Sep 17 00:00:00 2001 From: Steven Date: Sun, 26 Oct 2025 19:24:13 -0500 Subject: [PATCH 4/7] Update import paths and enhance notification services This commit updates the import paths in several files for improved clarity and organization. It modifies the import statement in `next-env.d.ts` to reference the correct routes type definition, consolidates imports in `verify-controller.ts` for better readability, and refines the import structure in `useOrderNotifications.ts` and `order-fetcher.ts` to utilize server-specific notification services. Additionally, it adds a note in `index.ts` to clarify the server-only nature of certain exports, ensuring a cleaner and more maintainable codebase. --- .../_lib/controllers/verify-controller.ts | 3 +-- .../notifications/useOrderNotifications.ts | 7 ++++-- next-env.d.ts | 2 +- .../services/fetchers/order/order-fetcher.ts | 2 +- .../services/notifications/index.ts | 5 ++-- .../services/notifications/server.ts | 23 +++++++++++++++++++ 6 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 packages/liquid-forge/services/notifications/server.ts diff --git a/app/api/domain-validation/_lib/controllers/verify-controller.ts b/app/api/domain-validation/_lib/controllers/verify-controller.ts index 3a63263e..0b995969 100644 --- a/app/api/domain-validation/_lib/controllers/verify-controller.ts +++ b/app/api/domain-validation/_lib/controllers/verify-controller.ts @@ -16,8 +16,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { getNextCorsHeaders } from '@/lib/utils/next-cors'; -import { CustomDomainService } from '@fasttify/tenant-domains'; -import { SecurityConfig } from '@fasttify/tenant-domains'; +import { CustomDomainService, SecurityConfig } from '@fasttify/tenant-domains'; import { SecureLogger } from '@/lib/utils/secure-logger'; const customDomainService = new CustomDomainService(); diff --git a/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts b/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts index 7007dc81..f89da716 100644 --- a/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts +++ b/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts @@ -1,8 +1,11 @@ import { useCallback } from 'react'; import { useEmailNotifications } from '@/app/store/hooks/api/useEmailNotifications'; import type { OrderStatus, PaymentStatus } from '@/app/store/hooks/data/useOrders/types'; -import { EmailFormattingUtils } from '@fasttify/liquid-forge/services/notifications'; -import { getOrderStatus, getPaymentStatus } from '@fasttify/liquid-forge/services/notifications/status-translations'; +import { + EmailFormattingUtils, + getOrderStatus, + getPaymentStatus, +} from '@fasttify/liquid-forge/services/notifications/client-utils'; /** * Hook para manejar las notificaciones de email relacionadas con órdenes diff --git a/next-env.d.ts b/next-env.d.ts index c4b7818f..9edff1c7 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/packages/liquid-forge/services/fetchers/order/order-fetcher.ts b/packages/liquid-forge/services/fetchers/order/order-fetcher.ts index 5b6af23f..a87eb42e 100644 --- a/packages/liquid-forge/services/fetchers/order/order-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/order/order-fetcher.ts @@ -18,7 +18,7 @@ import { orderNumberGenerator } from '@/liquid-forge/services/fetchers/order/ord import { orderValidator } from '@/liquid-forge/services/fetchers/order/order-validator'; import type { CreateOrderRequest, CreateOrderResponse } from './types/order-types'; import { EmailOrderService } from '@/liquid-forge/services/notifications/email-order-service'; -import { notificationCreator } from '@/liquid-forge/services/notifications'; +import { notificationCreator } from '@/liquid-forge/services/notifications/server'; import { analyticsWebhookService } from '@/liquid-forge/services/analytics'; export class OrderFetcher { diff --git a/packages/liquid-forge/services/notifications/index.ts b/packages/liquid-forge/services/notifications/index.ts index c841e881..83142ae7 100644 --- a/packages/liquid-forge/services/notifications/index.ts +++ b/packages/liquid-forge/services/notifications/index.ts @@ -30,6 +30,7 @@ export type { OrderConfirmationEmailRequest, } from './email-notification-service'; -// Exportar servicios de notificación para el panel admin -export { NotificationCreator, notificationCreator } from './notification-creator'; +// NOTE: NotificationCreator y notificationCreator son server-only +// y se exportan desde './server.ts' para evitar que sean incluidos en bundles del cliente + export type { CreateAdminNotificationRequest } from './types/notification-types'; diff --git a/packages/liquid-forge/services/notifications/server.ts b/packages/liquid-forge/services/notifications/server.ts new file mode 100644 index 00000000..2f5eb69e --- /dev/null +++ b/packages/liquid-forge/services/notifications/server.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Server-only exports + * + * Este archivo exporta código que SOLO puede usarse en el servidor + * porque depende de APIs del servidor (next/headers, etc). + * + * NO importar estos desde componentes del cliente. + */ + +export { NotificationCreator, notificationCreator } from './notification-creator'; +export type { CreateAdminNotificationRequest } from './types/notification-types'; From d8a3dc64d2b2192e4078d0b5b2057e2a0b57c982 Mon Sep 17 00:00:00 2001 From: Steven Date: Sun, 26 Oct 2025 19:38:34 -0500 Subject: [PATCH 5/7] Add transpilePackages to next.config.ts for improved package handling --- next.config.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/next.config.ts b/next.config.ts index be00efae..d70f5890 100644 --- a/next.config.ts +++ b/next.config.ts @@ -7,6 +7,13 @@ const nextConfig: NextConfig = { ignoreBuildErrors: true, }, + transpilePackages: [ + '@fasttify/liquid-forge', + '@fasttify/tenant-domains', + '@fasttify/orders-app', + '@fasttify/theme-editor', + ], + serverExternalPackages: [ '@aws-sdk/client-acm', '@aws-sdk/client-bedrock-runtime', From 4105b7c95a7e2671a31bc1f92729cfdca6c16f8c Mon Sep 17 00:00:00 2001 From: Steven Date: Sun, 26 Oct 2025 20:20:40 -0500 Subject: [PATCH 6/7] Update import paths to use relative references and add new scripts for import conversion This commit updates various import paths across the liquid-forge package to use relative references instead of absolute paths, enhancing clarity and maintainability. Additionally, it introduces new scripts in the package.json for converting imports to relative paths, facilitating easier refactoring in the future. The pnpm-lock.yaml file is also updated to include the tsx package for running the conversion scripts. --- packages/liquid-forge/config/page-config.ts | 4 +- .../liquid-forge/config/route-matchers.ts | 2 +- .../factories/store-renderer-factory.ts | 8 +- packages/liquid-forge/index.ts | 2 +- packages/liquid-forge/instances.ts | 2 +- packages/liquid-forge/liquid/engine.ts | 8 +- .../liquid/filters/base-filters.ts | 2 +- .../liquid/filters/cart-filters.ts | 2 +- .../liquid/filters/data-access-filters.ts | 2 +- .../liquid/filters/ecommerce-filters.ts | 2 +- .../liquid/filters/html-filters.ts | 6 +- .../liquid/filters/money-filters.ts | 2 +- .../liquid/tags/core/paginate-tag.ts | 2 +- .../liquid/tags/core/render-tag.ts | 5 +- .../liquid/tags/core/section-tag.ts | 2 +- .../liquid/tags/core/sections-tag.ts | 2 +- .../liquid/tags/data/javascript-tag.ts | 4 +- .../liquid/tags/data/schema-tag.ts | 2 +- .../liquid/tags/filters/data-fetcher.ts | 2 +- .../liquid/tags/styling/style-tag.ts | 4 +- packages/liquid-forge/package.json | 17 +- .../renderers/dynamic-page-renderer.ts | 32 +- .../pipeline-steps/build-context-step.ts | 4 +- .../pipeline-steps/initialize-engine-step.ts | 4 +- .../pipeline-steps/load-data-step.ts | 12 +- .../pipeline-steps/render-content-step.ts | 6 +- .../pipeline-steps/resolve-store-step.ts | 4 +- .../scripts/convert-imports-to-relative.ts | 331 ++++++++++++++++++ .../analytics/analytics-webhook.service.ts | 2 +- .../analytics/store-views-tracker.service.ts | 2 +- .../core/cache/cache-invalidation-service.ts | 4 +- .../services/core/cache/cache-manager.ts | 2 +- .../services/core/domain-resolver.ts | 6 +- .../services/core/navigation-service.ts | 6 +- .../services/errors/error-messages.ts | 2 +- .../services/errors/error-renderer.ts | 10 +- .../services/errors/error-utils.ts | 2 +- .../fetchers/cart/cart-context-transformer.ts | 4 +- .../services/fetchers/cart/cart-fetcher.ts | 6 +- .../fetchers/cart/cart-stock-validator.ts | 4 +- .../checkout/checkout-data-transformer.ts | 4 +- .../fetchers/checkout/checkout-fetcher.ts | 4 +- .../checkout/checkout-order-creator.ts | 4 +- .../checkout/checkout-session-manager.ts | 2 +- .../collection/collection-cache-manager.ts | 2 +- .../fetchers/collection/collection-fetcher.ts | 4 +- .../collection/collection-transformer.ts | 4 +- .../collection/types/collection-types.ts | 2 +- .../services/fetchers/data-fetcher.ts | 16 +- .../navigation/navigation-cache-manager.ts | 2 +- .../fetchers/navigation/navigation-fetcher.ts | 4 +- .../navigation/navigation-menu-processor.ts | 4 +- .../navigation/navigation-url-resolver.ts | 2 +- .../services/fetchers/order/order-fetcher.ts | 16 +- .../fetchers/order/order-item-creator.ts | 2 +- .../fetchers/page/page-cache-manager.ts | 2 +- .../services/fetchers/page/page-fetcher.ts | 4 +- .../fetchers/page/page-transformer.ts | 2 +- .../fetchers/page/types/page-types.ts | 2 +- .../fetchers/product/product-cache-manager.ts | 2 +- .../fetchers/product/product-fetcher.ts | 4 +- .../product/product-handle-manager.ts | 2 +- .../fetchers/product/product-transformer.ts | 4 +- .../fetchers/product/types/product-types.ts | 2 +- .../email-notification-service.ts | 2 +- .../notifications/email-order-service.ts | 2 +- .../notifications/notification-creator.ts | 4 +- .../notifications/types/notification-types.ts | 2 +- .../core/context-builder-helper.ts | 2 +- .../page/data-loader/core/core-data-loader.ts | 14 +- .../core/template-analyzer-helper.ts | 12 +- .../page/data-loader/data-fetcher-helper.ts | 6 +- .../data-loader/handlers/data-handlers.ts | 10 +- .../handlers/response-processors.ts | 4 +- .../pagination/pagination-builder-helper.ts | 4 +- .../pagination/pagination-limit-extractor.ts | 6 +- .../data-loader/search/search-data-loader.ts | 10 +- .../search/search-limits-extractor.ts | 2 +- .../services/page/dynamic-data-loader.ts | 12 +- .../services/rendering/global-context.ts | 6 +- .../services/rendering/metadata-generator.ts | 2 +- .../services/rendering/page-html-cache.ts | 6 +- .../services/rendering/render-page-content.ts | 10 +- .../services/rendering/section-renderer.ts | 10 +- .../templates/analysis/template-analyzer.ts | 6 +- .../parsing/liquid-syntax-detector.ts | 6 +- .../sync/template-dev-synchronizer.ts | 10 +- .../services/templates/template-loader.ts | 6 +- .../services/themes/core/theme-processor.ts | 6 +- .../themes/storage/s3-storage-service.ts | 2 +- .../validation/rules/liquid-syntax-rules.ts | 2 +- .../themes/validation/validation-engine.ts | 2 +- packages/liquid-forge/types/template.ts | 2 +- pnpm-lock.yaml | 7 +- 94 files changed, 562 insertions(+), 234 deletions(-) create mode 100644 packages/liquid-forge/scripts/convert-imports-to-relative.ts diff --git a/packages/liquid-forge/config/page-config.ts b/packages/liquid-forge/config/page-config.ts index 7f07c95b..e7485910 100644 --- a/packages/liquid-forge/config/page-config.ts +++ b/packages/liquid-forge/config/page-config.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { cacheManager } from '@/liquid-forge/services/core/cache'; -import type { PageType } from '@/liquid-forge/types/template'; +import { cacheManager } from '../services/core/cache'; +import type { PageType } from '../types/template'; const templatePaths: Record = { index: 'templates/index.json', diff --git a/packages/liquid-forge/config/route-matchers.ts b/packages/liquid-forge/config/route-matchers.ts index dae97e55..dcc007ff 100644 --- a/packages/liquid-forge/config/route-matchers.ts +++ b/packages/liquid-forge/config/route-matchers.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import type { PageRenderOptions } from '../types/template'; /** * Tipo para matchers de rutas diff --git a/packages/liquid-forge/factories/store-renderer-factory.ts b/packages/liquid-forge/factories/store-renderer-factory.ts index 255fb65c..e784f622 100644 --- a/packages/liquid-forge/factories/store-renderer-factory.ts +++ b/packages/liquid-forge/factories/store-renderer-factory.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { pathToRenderOptions } from '@/liquid-forge/config/route-matchers'; -import { logger } from '@/liquid-forge/lib/logger'; -import { DynamicPageRenderer } from '@/liquid-forge/renderers/dynamic-page-renderer'; -import type { RenderResult } from '@/liquid-forge/types'; +import { pathToRenderOptions } from '../config/route-matchers'; +import { logger } from '../lib/logger'; +import { DynamicPageRenderer } from '../renderers/dynamic-page-renderer'; +import type { RenderResult } from '../types'; /** * Factory principal del sistema de renderizado de tiendas diff --git a/packages/liquid-forge/index.ts b/packages/liquid-forge/index.ts index 22c5d7f3..eac6b028 100644 --- a/packages/liquid-forge/index.ts +++ b/packages/liquid-forge/index.ts @@ -23,7 +23,7 @@ export * from './services/core/cache'; * * @example * ```typescript - * import { storeRenderer } from '@/liquid-forge'; + * import { storeRenderer } from './'; * * const result = await storeRenderer.renderPage('mystore.com', '/products/my-product'); * ``` diff --git a/packages/liquid-forge/instances.ts b/packages/liquid-forge/instances.ts index fb194f9a..789f661f 100644 --- a/packages/liquid-forge/instances.ts +++ b/packages/liquid-forge/instances.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { StoreRendererFactory } from '@/liquid-forge/factories/store-renderer-factory'; +import { StoreRendererFactory } from './factories/store-renderer-factory'; /** * Instancia singleton del renderizador de tiendas diff --git a/packages/liquid-forge/liquid/engine.ts b/packages/liquid-forge/liquid/engine.ts index 07f50a5c..b4a5b6dd 100644 --- a/packages/liquid-forge/liquid/engine.ts +++ b/packages/liquid-forge/liquid/engine.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { allFilters } from '@/liquid-forge/liquid/filters'; +import { allFilters } from './filters'; import { FiltersTag, FormTag, @@ -28,9 +28,9 @@ import { SectionsTag, StyleTag, StylesheetTag, -} from '@/liquid-forge/liquid/tags'; -import { AssetCollector } from '@/liquid-forge/services/rendering/asset-collector'; -import type { LiquidContext, LiquidEngineConfig, TemplateError } from '@/liquid-forge/types'; +} from './tags'; +import { AssetCollector } from '../services/rendering/asset-collector'; +import type { LiquidContext, LiquidEngineConfig, TemplateError } from '../types'; import { Liquid, Template } from 'liquidjs'; class LiquidEngine { diff --git a/packages/liquid-forge/liquid/filters/base-filters.ts b/packages/liquid-forge/liquid/filters/base-filters.ts index bbcc568b..28b70d20 100644 --- a/packages/liquid-forge/liquid/filters/base-filters.ts +++ b/packages/liquid-forge/liquid/filters/base-filters.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { LiquidFilter } from '@/liquid-forge/types'; +import type { LiquidFilter } from '../../types'; /** * Filtro para formatear fechas diff --git a/packages/liquid-forge/liquid/filters/cart-filters.ts b/packages/liquid-forge/liquid/filters/cart-filters.ts index 8dd83222..dcd6a6e3 100644 --- a/packages/liquid-forge/liquid/filters/cart-filters.ts +++ b/packages/liquid-forge/liquid/filters/cart-filters.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { LiquidFilter } from '@/liquid-forge/types'; +import type { LiquidFilter } from '../../types'; /** * Filtro cart_url - Genera URL del carrito diff --git a/packages/liquid-forge/liquid/filters/data-access-filters.ts b/packages/liquid-forge/liquid/filters/data-access-filters.ts index 43269222..9c851b06 100644 --- a/packages/liquid-forge/liquid/filters/data-access-filters.ts +++ b/packages/liquid-forge/liquid/filters/data-access-filters.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { LiquidFilter } from '@/liquid-forge/types'; +import type { LiquidFilter } from '../../types'; /** * Filtro para obtener una colección específica por handle desde el contexto diff --git a/packages/liquid-forge/liquid/filters/ecommerce-filters.ts b/packages/liquid-forge/liquid/filters/ecommerce-filters.ts index 94fa0d25..c820bcb3 100644 --- a/packages/liquid-forge/liquid/filters/ecommerce-filters.ts +++ b/packages/liquid-forge/liquid/filters/ecommerce-filters.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { LiquidFilter } from '@/liquid-forge/types'; +import type { LiquidFilter } from '../../types'; /** * Interfaz para parámetros de optimización de imágenes diff --git a/packages/liquid-forge/liquid/filters/html-filters.ts b/packages/liquid-forge/liquid/filters/html-filters.ts index dfdeb167..4533a2b7 100644 --- a/packages/liquid-forge/liquid/filters/html-filters.ts +++ b/packages/liquid-forge/liquid/filters/html-filters.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { LiquidFilter } from '@/liquid-forge/types'; +import { logger } from '../../lib/logger'; +import type { LiquidFilter } from '../../types'; /** * Filtro asset_url - Para archivos estáticos (CSS, JS, imágenes de tema) @@ -213,7 +213,7 @@ export const inlineAssetContentFilter: LiquidFilter = { try { // Importar templateLoader dinámicamente para evitar dependencias circulares - const { templateLoader } = await import('@/liquid-forge/services/templates/template-loader'); + const { templateLoader } = await import('../../services/templates/template-loader'); // Cargar el contenido del asset const assetContent = await templateLoader.loadAsset(storeId, cleanFilename); diff --git a/packages/liquid-forge/liquid/filters/money-filters.ts b/packages/liquid-forge/liquid/filters/money-filters.ts index 7c505c00..3577ad11 100644 --- a/packages/liquid-forge/liquid/filters/money-filters.ts +++ b/packages/liquid-forge/liquid/filters/money-filters.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { LiquidFilter } from '@/liquid-forge/types'; +import type { LiquidFilter } from '../../types'; /** * Filtro para extraer el símbolo de la moneda diff --git a/packages/liquid-forge/liquid/tags/core/paginate-tag.ts b/packages/liquid-forge/liquid/tags/core/paginate-tag.ts index 4633816a..308e6505 100644 --- a/packages/liquid-forge/liquid/tags/core/paginate-tag.ts +++ b/packages/liquid-forge/liquid/tags/core/paginate-tag.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; import { Context, Emitter, Liquid, Tag, TagToken, Template, TopLevelToken } from 'liquidjs'; /** diff --git a/packages/liquid-forge/liquid/tags/core/render-tag.ts b/packages/liquid-forge/liquid/tags/core/render-tag.ts index 0f361357..a48fb67a 100644 --- a/packages/liquid-forge/liquid/tags/core/render-tag.ts +++ b/packages/liquid-forge/liquid/tags/core/render-tag.ts @@ -15,7 +15,7 @@ */ import { Tag, TagToken, Context, TopLevelToken, Liquid } from 'liquidjs'; -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; /** * Custom Render Tag para manejar {% render 'snippet' %} en LiquidJS @@ -121,8 +121,7 @@ export class RenderTag extends Tag { } // Usar el TemplateLoader para cargar el snippet - const { TemplateLoader } = await import('@/liquid-forge/services/templates/template-loader'); - const templateLoader = TemplateLoader.getInstance(); + const { templateLoader } = await import('../../../services/templates/template-loader'); // Los snippets están en la carpeta 'snippets' const snippetFileName = snippetName.endsWith('.liquid') ? snippetName : `${snippetName}.liquid`; diff --git a/packages/liquid-forge/liquid/tags/core/section-tag.ts b/packages/liquid-forge/liquid/tags/core/section-tag.ts index 4f26bdb9..77b457f1 100644 --- a/packages/liquid-forge/liquid/tags/core/section-tag.ts +++ b/packages/liquid-forge/liquid/tags/core/section-tag.ts @@ -15,7 +15,7 @@ */ import { Tag, TagToken, Context, TopLevelToken, Liquid } from 'liquidjs'; -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; /** * Custom Section Tag para manejar {% section 'section-name' %} en LiquidJS diff --git a/packages/liquid-forge/liquid/tags/core/sections-tag.ts b/packages/liquid-forge/liquid/tags/core/sections-tag.ts index 3458a1d9..94e132e0 100644 --- a/packages/liquid-forge/liquid/tags/core/sections-tag.ts +++ b/packages/liquid-forge/liquid/tags/core/sections-tag.ts @@ -15,7 +15,7 @@ */ import { Tag, TagToken, Context, TopLevelToken, Liquid } from 'liquidjs'; -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; /** * Custom Sections Tag para manejar {% sections 'section-group' %} en LiquidJS diff --git a/packages/liquid-forge/liquid/tags/data/javascript-tag.ts b/packages/liquid-forge/liquid/tags/data/javascript-tag.ts index b50b9c32..08836d17 100644 --- a/packages/liquid-forge/liquid/tags/data/javascript-tag.ts +++ b/packages/liquid-forge/liquid/tags/data/javascript-tag.ts @@ -15,8 +15,8 @@ */ import { Tag, TagToken, Context, TopLevelToken, Liquid, TokenKind } from 'liquidjs'; -import { AssetCollector } from '@/liquid-forge/services/rendering/asset-collector'; -import { logger } from '@/liquid-forge/lib/logger'; +import { AssetCollector } from '../../../services/rendering/asset-collector'; +import { logger } from '../../../lib/logger'; /** * Optimiza y limpia el JavaScript generado diff --git a/packages/liquid-forge/liquid/tags/data/schema-tag.ts b/packages/liquid-forge/liquid/tags/data/schema-tag.ts index de7c6847..62f6c1d1 100644 --- a/packages/liquid-forge/liquid/tags/data/schema-tag.ts +++ b/packages/liquid-forge/liquid/tags/data/schema-tag.ts @@ -15,7 +15,7 @@ */ import { Tag, TagToken, Context, TopLevelToken, Liquid, TokenKind } from 'liquidjs'; -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; /** * Custom Schema Tag para manejar {% schema %} de Shopify en LiquidJS diff --git a/packages/liquid-forge/liquid/tags/filters/data-fetcher.ts b/packages/liquid-forge/liquid/tags/filters/data-fetcher.ts index b8c41a3b..982acfb0 100644 --- a/packages/liquid-forge/liquid/tags/filters/data-fetcher.ts +++ b/packages/liquid-forge/liquid/tags/filters/data-fetcher.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { dataFetcher } from '@/liquid-forge/services/fetchers/data-fetcher'; +import { dataFetcher } from '../../../services/fetchers/data-fetcher'; import { AvailableFilters } from './types'; export class FilterDataFetcher { diff --git a/packages/liquid-forge/liquid/tags/styling/style-tag.ts b/packages/liquid-forge/liquid/tags/styling/style-tag.ts index 67e73f1e..6636cc47 100644 --- a/packages/liquid-forge/liquid/tags/styling/style-tag.ts +++ b/packages/liquid-forge/liquid/tags/styling/style-tag.ts @@ -15,8 +15,8 @@ */ import { Tag, TagToken, Context, TopLevelToken, Liquid, TokenKind } from 'liquidjs'; -import { AssetCollector } from '@/liquid-forge/services/rendering/asset-collector'; -import { logger } from '@/liquid-forge/lib/logger'; +import { AssetCollector } from '../../../services/rendering/asset-collector'; +import { logger } from '../../../lib/logger'; /** * Optimiza y limpia el CSS generado diff --git a/packages/liquid-forge/package.json b/packages/liquid-forge/package.json index 38b47b55..3d387464 100644 --- a/packages/liquid-forge/package.json +++ b/packages/liquid-forge/package.json @@ -12,7 +12,9 @@ "test": "jest", "lint": "eslint .", "lint:fix": "eslint . --fix", - "type-check": "tsc --noEmit" + "type-check": "tsc --noEmit", + "convert-imports": "tsx scripts/convert-imports-to-relative.ts", + "convert-imports:dry": "tsx scripts/convert-imports-to-relative.ts --dry-run" }, "dependencies": { "browserslist": "^4.26.3", @@ -27,17 +29,18 @@ "cssnano": "^7.1.1", "eslint": "^8.0.0", "jest": "^30.0.4", + "tsx": "^4.20.6", "typescript": "^5.8.3" }, "peerDependencies": { - "dotenv": "^17.2.0", - "autoprefixer": "^10.4.21", - "postcss": "^8.5.6", - "@aws-sdk/lib-storage": "^3.903.0", "@aws-sdk/client-lambda": "^3.901.0", "@aws-sdk/client-s3": "^3.844.0", - "uuid": "^11.1.0", + "@aws-sdk/lib-storage": "^3.903.0", + "autoprefixer": "^10.4.21", + "dotenv": "^17.2.0", + "liquidjs": "^10.21.1", "node-cache": "^5.1.2", - "liquidjs": "^10.21.1" + "postcss": "^8.5.6", + "uuid": "^11.1.0" } } diff --git a/packages/liquid-forge/renderers/dynamic-page-renderer.ts b/packages/liquid-forge/renderers/dynamic-page-renderer.ts index ded4c1f2..e62e9d8f 100644 --- a/packages/liquid-forge/renderers/dynamic-page-renderer.ts +++ b/packages/liquid-forge/renderers/dynamic-page-renderer.ts @@ -15,26 +15,22 @@ */ // Core utilities -import { injectAssets } from '@/liquid-forge/lib/inject-assets'; -import { logger } from '@/liquid-forge/lib/logger'; +import { injectAssets } from '../lib/inject-assets'; +import { logger } from '../lib/logger'; // Liquid engine -import { liquidEngine } from '@/liquid-forge/liquid/engine'; +import { liquidEngine } from '../liquid/engine'; // Services -import { pageConfig } from '@/liquid-forge/config/page-config'; +import { pageConfig } from '../config/page-config'; // Clave y caché de HTML gestionados en utilidades dedicadas -import { - getCachedPageRender, - makePageCacheKey, - setCachedPageRender, -} from '@/liquid-forge/services/rendering/page-html-cache'; -import { domainResolver } from '@/liquid-forge/services/core/domain-resolver'; -import { errorRenderer } from '@/liquid-forge/services/errors/error-renderer'; -import { createTemplateError } from '@/liquid-forge/services/errors/error-utils'; -import { metadataGenerator } from '@/liquid-forge/services/rendering/metadata-generator'; -import { sectionRenderer } from '@/liquid-forge/services/rendering/section-renderer'; -import { templateLoader } from '@/liquid-forge/services/templates/template-loader'; +import { getCachedPageRender, makePageCacheKey, setCachedPageRender } from '../services/rendering/page-html-cache'; +import { domainResolver } from '../services/core/domain-resolver'; +import { errorRenderer } from '../services/errors/error-renderer'; +import { createTemplateError } from '../services/errors/error-utils'; +import { metadataGenerator } from '../services/rendering/metadata-generator'; +import { sectionRenderer } from '../services/rendering/section-renderer'; +import { templateLoader } from '../services/templates/template-loader'; // Pipeline steps import { @@ -43,11 +39,11 @@ import { loadDataStep, renderContentStep, resolveStoreStep, -} from '@/liquid-forge/renderers/pipeline-steps'; +} from './pipeline-steps'; // Types -import type { RenderResult, ShopContext, TemplateError } from '@/liquid-forge/types'; -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import type { RenderResult, ShopContext, TemplateError } from '../types'; +import type { PageRenderOptions } from '../types/template'; import type { Template } from 'liquidjs'; /** diff --git a/packages/liquid-forge/renderers/pipeline-steps/build-context-step.ts b/packages/liquid-forge/renderers/pipeline-steps/build-context-step.ts index 64f9c75e..6a3ee81a 100644 --- a/packages/liquid-forge/renderers/pipeline-steps/build-context-step.ts +++ b/packages/liquid-forge/renderers/pipeline-steps/build-context-step.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import type { RenderingData } from '@/liquid-forge/renderers/dynamic-page-renderer'; -import { contextBuilder } from '@/liquid-forge/services/rendering/global-context'; +import type { RenderingData } from '../dynamic-page-renderer'; +import { contextBuilder } from '../../services/rendering/global-context'; /** * Helper para exponer solo propiedades definidas diff --git a/packages/liquid-forge/renderers/pipeline-steps/initialize-engine-step.ts b/packages/liquid-forge/renderers/pipeline-steps/initialize-engine-step.ts index 19d7922e..38e7841a 100644 --- a/packages/liquid-forge/renderers/pipeline-steps/initialize-engine-step.ts +++ b/packages/liquid-forge/renderers/pipeline-steps/initialize-engine-step.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { liquidEngine } from '@/liquid-forge/liquid/engine'; -import type { RenderingData } from '@/liquid-forge/renderers/dynamic-page-renderer'; +import { liquidEngine } from '../../liquid/engine'; +import type { RenderingData } from '../dynamic-page-renderer'; /** * Paso 2: Inicializar motor de rendering diff --git a/packages/liquid-forge/renderers/pipeline-steps/load-data-step.ts b/packages/liquid-forge/renderers/pipeline-steps/load-data-step.ts index 54eeb95e..bebe8618 100644 --- a/packages/liquid-forge/renderers/pipeline-steps/load-data-step.ts +++ b/packages/liquid-forge/renderers/pipeline-steps/load-data-step.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { pageConfig } from '@/liquid-forge/config/page-config'; -import { logger } from '@/liquid-forge/lib/logger'; -import type { RenderingData } from '@/liquid-forge/renderers/dynamic-page-renderer'; -import { dataFetcher } from '@/liquid-forge/services/fetchers/data-fetcher'; -import { dynamicDataLoader } from '@/liquid-forge/services/page/dynamic-data-loader'; -import { templateLoader } from '@/liquid-forge/services/templates/template-loader'; +import { pageConfig } from '../../config/page-config'; +import { logger } from '../../lib/logger'; +import type { RenderingData } from '../dynamic-page-renderer'; +import { dataFetcher } from '../../services/fetchers/data-fetcher'; +import { dynamicDataLoader } from '../../services/page/dynamic-data-loader'; +import { templateLoader } from '../../services/templates/template-loader'; /** * Paso 4: Cargar todos los datos en paralelo diff --git a/packages/liquid-forge/renderers/pipeline-steps/render-content-step.ts b/packages/liquid-forge/renderers/pipeline-steps/render-content-step.ts index 950593e2..d8b845b2 100644 --- a/packages/liquid-forge/renderers/pipeline-steps/render-content-step.ts +++ b/packages/liquid-forge/renderers/pipeline-steps/render-content-step.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { pageConfig } from '@/liquid-forge/config/page-config'; -import type { RenderingData } from '@/liquid-forge/renderers/dynamic-page-renderer'; -import { renderPageContent } from '@/liquid-forge/services/rendering/render-page-content'; +import { pageConfig } from '../../config/page-config'; +import type { RenderingData } from '../dynamic-page-renderer'; +import { renderPageContent } from '../../services/rendering/render-page-content'; /** * Paso 6: Renderizar contenido de la página diff --git a/packages/liquid-forge/renderers/pipeline-steps/resolve-store-step.ts b/packages/liquid-forge/renderers/pipeline-steps/resolve-store-step.ts index b1feede1..1f78e8f5 100644 --- a/packages/liquid-forge/renderers/pipeline-steps/resolve-store-step.ts +++ b/packages/liquid-forge/renderers/pipeline-steps/resolve-store-step.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import type { RenderingData } from '@/liquid-forge/renderers/dynamic-page-renderer'; -import { domainResolver } from '@/liquid-forge/services/core/domain-resolver'; +import type { RenderingData } from '../dynamic-page-renderer'; +import { domainResolver } from '../../services/core/domain-resolver'; /** * Paso 1: Resolver dominio a tienda diff --git a/packages/liquid-forge/scripts/convert-imports-to-relative.ts b/packages/liquid-forge/scripts/convert-imports-to-relative.ts new file mode 100644 index 00000000..00a7e77a --- /dev/null +++ b/packages/liquid-forge/scripts/convert-imports-to-relative.ts @@ -0,0 +1,331 @@ +#!/usr/bin/env node +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; +import path from 'path'; + +/** + * Configuración de los alias del proyecto + * Solo convertimos aliases que apuntan dentro del paquete (@/liquid-forge y @/) + * Los demás se mantienen como alias ya que apuntan fuera del paquete + */ +const ALIAS_CONFIG: Record = { + '@/liquid-forge': '.', + '@/': '.', +}; + +/** + * Aliases que NO se deben convertir (rutas externas al paquete) + */ +const EXTERNAL_ALIASES = ['@/utils', '@/lib', '@/amplify', '@/data-schema', '@/amplify_outputs.json', '@/api']; + +/** + * Verifica si un import es de fuera del paquete + */ +function isExternalAlias(importPath: string): boolean { + return EXTERNAL_ALIASES.some((alias) => importPath.startsWith(alias)); +} + +/** + * Tipos de archivos que procesar + */ +const FILE_EXTENSIONS = ['.ts', '.tsx']; + +/** + * Directorios a excluir + */ +const EXCLUDE_DIRS = ['node_modules', 'dist', '.git', 'scripts', '__tests__', 'test', 'tests']; + +/** + * Estadísticas del proceso + */ +interface ProcessStats { + filesScanned: number; + filesModified: number; + importsConverted: number; + errors: number; +} + +/** + * Verifica si una ruta debe ser excluida + */ +function shouldExclude(filePath: string): boolean { + const pathParts = filePath.split(path.sep); + return EXCLUDE_DIRS.some((excludeDir) => pathParts.includes(excludeDir)); +} + +/** + * Convierte un import con alias a una ruta relativa + */ +function convertAliasToRelative(importPath: string, fromFile: string, baseDir: string): string { + // Buscar el alias más largo que coincida + let matchedAlias = ''; + let matchedPath = ''; + + for (const [alias, target] of Object.entries(ALIAS_CONFIG)) { + if (importPath.startsWith(alias)) { + if (alias.length > matchedAlias.length) { + matchedAlias = alias; + matchedPath = target; + } + } + } + + if (!matchedAlias) { + return importPath; // No hay alias, mantener el import original + } + + // Remover el alias del path + const remainingPath = importPath.substring(matchedAlias.length); + + // Construir la ruta completa del alias + // Si el path empieza con /, removerlo + const cleanRemaining = remainingPath.startsWith('/') ? remainingPath.substring(1) : remainingPath; + const fullTargetPath = path.resolve(baseDir, matchedPath, cleanRemaining); + + // Obtener la ruta relativa desde el archivo origen + const relativePath = path.relative(path.dirname(fromFile), fullTargetPath); + + // Normalizar la ruta relativa para import statements + let normalizedPath = relativePath.replace(/\\/g, '/'); + + // Asegurar que las rutas relativas empiecen con ./ + if (!normalizedPath.startsWith('.') && !normalizedPath.startsWith('@')) { + normalizedPath = './' + normalizedPath; + } + + // Remover la extensión .ts o .tsx + normalizedPath = normalizedPath.replace(/\.tsx?$/, ''); + + return normalizedPath; +} + +/** + * Verifica si una ruta de archivo existe + */ +function fileExists(filePath: string): boolean { + try { + // Probar con diferentes extensiones + const extensions = ['', '.ts', '.tsx', '.js', '.jsx', '.json']; + for (const ext of extensions) { + if (fs.existsSync(filePath + ext)) { + return true; + } + } + return false; + } catch { + return false; + } +} + +/** + * Convierte imports en un archivo + */ +function convertFileImports( + filePath: string, + baseDir: string, + dryRun: boolean = false +): { modified: boolean; importsConverted: number; changes: Array<{ old: string; new: string }> } { + try { + const content = fs.readFileSync(filePath, 'utf-8'); + const lines = content.split('\n'); + let modified = false; + let importsConverted = 0; + const changes: Array<{ old: string; new: string }> = []; + + // Procesar el contenido completo para capturar imports multi-línea + const contentStr = lines.join('\n'); + + // Buscar todos los imports estáticos (pueden ser multi-línea) + const staticImportRegex = /import\s+(?:[^'"]*?from\s+['"])([@\/\w_-]+)(['"])/g; + // Buscar imports dinámicos (import(), require(), etc.) + const dynamicImportRegex = /(?:import|require)\s*\(['"]([@\/\w_-]+)['"]\)/g; + + let newContent = contentStr; + + // Procesar imports estáticos + newContent = contentStr.replace(staticImportRegex, (match, importPath) => { + // NO convertir imports externos al paquete + if (isExternalAlias(importPath)) { + return match; + } + + // Verificar si es un alias que necesitamos convertir + let shouldConvert = false; + for (const alias of Object.keys(ALIAS_CONFIG)) { + if (importPath.startsWith(alias)) { + shouldConvert = true; + break; + } + } + + if (shouldConvert) { + const relativePath = convertAliasToRelative(importPath, filePath, baseDir); + + if (relativePath !== importPath) { + modified = true; + importsConverted++; + + changes.push({ old: importPath, new: relativePath }); + + return match.replace(importPath, relativePath); + } + } + + return match; + }); + + // Procesar imports dinámicos + newContent = newContent.replace(dynamicImportRegex, (match, importPath) => { + // NO convertir imports externos al paquete + if (isExternalAlias(importPath)) { + return match; + } + + // Verificar si es un alias que necesitamos convertir + let shouldConvert = false; + for (const alias of Object.keys(ALIAS_CONFIG)) { + if (importPath.startsWith(alias)) { + shouldConvert = true; + break; + } + } + + if (shouldConvert) { + const relativePath = convertAliasToRelative(importPath, filePath, baseDir); + + if (relativePath !== importPath) { + modified = true; + importsConverted++; + + changes.push({ old: importPath, new: relativePath }); + + return match.replace(importPath, relativePath); + } + } + + return match; + }); + + const newLines = newContent.split('\n'); + + if (modified && !dryRun) { + fs.writeFileSync(filePath, newLines.join('\n'), 'utf-8'); + console.log(`Convertido: ${path.relative(baseDir, filePath)} (${importsConverted} imports)`); + } else if (modified && dryRun) { + console.log(` - ${path.relative(baseDir, filePath)} (${importsConverted} imports)`); + } + + return { modified, importsConverted, changes }; + } catch (error) { + console.error(`Error procesando ${filePath}:`, error); + return { modified: false, importsConverted: 0, changes: [] }; + } +} + +/** + * Encuentra todos los archivos TypeScript en un directorio + */ +function findTsFiles(dir: string, baseDir: string, fileList: string[] = []): string[] { + if (!fs.existsSync(dir)) { + return fileList; + } + + const items = fs.readdirSync(dir); + + for (const item of items) { + const fullPath = path.join(dir, item); + + if (shouldExclude(fullPath)) { + continue; + } + + const stats = fs.statSync(fullPath); + + if (stats.isDirectory()) { + findTsFiles(fullPath, baseDir, fileList); + } else if (stats.isFile()) { + const ext = path.extname(item); + if (FILE_EXTENSIONS.includes(ext)) { + fileList.push(fullPath); + } + } + } + + return fileList; +} + +/** + * Función principal + */ +function main() { + const args = process.argv.slice(2); + const dryRun = args.includes('--dry-run') || args.includes('-d'); + + const packageDir = path.resolve(__dirname, '..'); + const stats: ProcessStats = { + filesScanned: 0, + filesModified: 0, + importsConverted: 0, + errors: 0, + }; + + if (dryRun) { + console.log('Modo DRY-RUN: No se realizarán cambios\n'); + } else { + console.log('Iniciando conversión de imports...\n'); + } + + console.log(`Directorio del paquete: ${packageDir}\n`); + + // Encontrar todos los archivos TypeScript + const files = findTsFiles(packageDir, packageDir); + stats.filesScanned = files.length; + + console.log(`${files.length} archivos encontrados\n`); + + // Procesar cada archivo + for (const file of files) { + const result = convertFileImports(file, packageDir, dryRun); + + if (result.modified) { + stats.filesModified++; + stats.importsConverted += result.importsConverted; + } + } + + // Mostrar estadísticas + console.log('\nConversión completada\n'); + console.log('Estadísticas:'); + console.log(` • Archivos escaneados: ${stats.filesScanned}`); + console.log(` • Archivos que serían modificados: ${stats.filesModified}`); + console.log(` • Imports convertidos: ${stats.importsConverted}`); + console.log(` • Errores: ${stats.errors}`); + + if (dryRun) { + console.log('\nPara aplicar los cambios, ejecuta sin --dry-run'); + } else if (stats.filesModified > 0) { + console.log('\nRecomendación: Revisa los cambios con git diff antes de hacer commit'); + } +} + +// Ejecutar si es llamado directamente +if (require.main === module) { + main(); +} + +export { convertAliasToRelative, ALIAS_CONFIG }; diff --git a/packages/liquid-forge/services/analytics/analytics-webhook.service.ts b/packages/liquid-forge/services/analytics/analytics-webhook.service.ts index 5409756f..c6496ef4 100644 --- a/packages/liquid-forge/services/analytics/analytics-webhook.service.ts +++ b/packages/liquid-forge/services/analytics/analytics-webhook.service.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../lib/logger'; import { AnalyticsWebhookJWTAuth } from '@/api/webhooks/_lib/middleware/jwt-auth.middleware'; /** diff --git a/packages/liquid-forge/services/analytics/store-views-tracker.service.ts b/packages/liquid-forge/services/analytics/store-views-tracker.service.ts index 615b8c63..008935bf 100644 --- a/packages/liquid-forge/services/analytics/store-views-tracker.service.ts +++ b/packages/liquid-forge/services/analytics/store-views-tracker.service.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../lib/logger'; import { randomUUID } from 'crypto'; import { AnalyticsWebhookJWTAuth } from '@/api/webhooks/_lib/middleware/jwt-auth.middleware'; diff --git a/packages/liquid-forge/services/core/cache/cache-invalidation-service.ts b/packages/liquid-forge/services/core/cache/cache-invalidation-service.ts index 90f1ea5e..7367fa2a 100644 --- a/packages/liquid-forge/services/core/cache/cache-invalidation-service.ts +++ b/packages/liquid-forge/services/core/cache/cache-invalidation-service.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; import { cacheManager, getCollectionPrefix, @@ -29,7 +29,7 @@ import { getProductsPrefix, getNavigationPrefix, getNavigationMenuPrefix, -} from '@/liquid-forge/services/core/cache'; +} from './'; /** * Tipos de cambios que pueden ocurrir en una tienda diff --git a/packages/liquid-forge/services/core/cache/cache-manager.ts b/packages/liquid-forge/services/core/cache/cache-manager.ts index 2a454a0a..63639cb6 100644 --- a/packages/liquid-forge/services/core/cache/cache-manager.ts +++ b/packages/liquid-forge/services/core/cache/cache-manager.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; import NodeCache from 'node-cache'; // Configuración de TTLs por categoría diff --git a/packages/liquid-forge/services/core/domain-resolver.ts b/packages/liquid-forge/services/core/domain-resolver.ts index b5d84f7d..bded2985 100644 --- a/packages/liquid-forge/services/core/domain-resolver.ts +++ b/packages/liquid-forge/services/core/domain-resolver.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { cacheManager, getDomainCacheKey } from '@/liquid-forge/services/core/cache'; -import { logger } from '@/liquid-forge/lib/logger'; -import type { Store, TemplateError } from '@/liquid-forge/types'; +import { cacheManager, getDomainCacheKey } from './cache'; +import { logger } from '../../lib/logger'; +import type { Store, TemplateError } from '../../types'; import { cookiesClient } from '@/utils/server/AmplifyServer'; class DomainResolver { diff --git a/packages/liquid-forge/services/core/navigation-service.ts b/packages/liquid-forge/services/core/navigation-service.ts index 95e224bb..b7e988e4 100644 --- a/packages/liquid-forge/services/core/navigation-service.ts +++ b/packages/liquid-forge/services/core/navigation-service.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { dataFetcher } from '@/liquid-forge/services/fetchers/data-fetcher'; -import type { ProcessedNavigationMenu } from '@/liquid-forge/types/store'; -import { logger } from '@/liquid-forge/lib/logger'; +import { dataFetcher } from '../fetchers/data-fetcher'; +import type { ProcessedNavigationMenu } from '../../types/store'; +import { logger } from '../../lib/logger'; export interface LinkListItem { title: string; diff --git a/packages/liquid-forge/services/errors/error-messages.ts b/packages/liquid-forge/services/errors/error-messages.ts index 7fd2ea11..982e7770 100644 --- a/packages/liquid-forge/services/errors/error-messages.ts +++ b/packages/liquid-forge/services/errors/error-messages.ts @@ -15,7 +15,7 @@ */ // Helpers para mensajes, títulos, descripciones y sugerencias de errores -import type { TemplateError } from '@/liquid-forge/types'; +import type { TemplateError } from '../../types'; export function getFriendlyMessage(errorType: TemplateError['type']): string { const messages = { diff --git a/packages/liquid-forge/services/errors/error-renderer.ts b/packages/liquid-forge/services/errors/error-renderer.ts index ffd8d24c..8fc52064 100644 --- a/packages/liquid-forge/services/errors/error-renderer.ts +++ b/packages/liquid-forge/services/errors/error-renderer.ts @@ -14,16 +14,16 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { liquidEngine } from '@/liquid-forge/liquid/engine'; -import type { RenderResult, ShopContext, TemplateError } from '@/liquid-forge/types'; +import { logger } from '../../lib/logger'; +import { liquidEngine } from '../../liquid/engine'; +import type { RenderResult, ShopContext, TemplateError } from '../../types'; import { extractStoreName, getErrorDescription, getErrorSuggestions, getErrorTitle, getFriendlyMessage, -} from '@/liquid-forge/services/errors/error-messages'; +} from './error-messages'; import { getDataErrorTemplate, getFallbackErrorPageTemplate, @@ -31,7 +31,7 @@ import { getStoreNotActiveTemplate, getStoreNotFoundTemplate, getTemplateNotFoundTemplate, -} from '@/liquid-forge/services/errors/error-templates'; +} from './error-templates'; export interface ErrorRenderOptions { storeId?: string; diff --git a/packages/liquid-forge/services/errors/error-utils.ts b/packages/liquid-forge/services/errors/error-utils.ts index 11d54d96..a9097484 100644 --- a/packages/liquid-forge/services/errors/error-utils.ts +++ b/packages/liquid-forge/services/errors/error-utils.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { TemplateError } from '@/liquid-forge/types'; +import type { TemplateError } from '../../types'; /** * Crea un objeto TemplateError estándar diff --git a/packages/liquid-forge/services/fetchers/cart/cart-context-transformer.ts b/packages/liquid-forge/services/fetchers/cart/cart-context-transformer.ts index ee928332..c4be5b82 100644 --- a/packages/liquid-forge/services/fetchers/cart/cart-context-transformer.ts +++ b/packages/liquid-forge/services/fetchers/cart/cart-context-transformer.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { Cart, CartContext } from '@/liquid-forge/types'; +import { logger } from '../../../lib/logger'; +import type { Cart, CartContext } from '../../../types'; export class CartContextTransformer { /** diff --git a/packages/liquid-forge/services/fetchers/cart/cart-fetcher.ts b/packages/liquid-forge/services/fetchers/cart/cart-fetcher.ts index c06a32bb..84455c8b 100644 --- a/packages/liquid-forge/services/fetchers/cart/cart-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/cart/cart-fetcher.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { dataFetcher } from '@/liquid-forge/services/fetchers/data-fetcher'; -import type { Cart, CartRaw } from '@/liquid-forge/types'; +import { logger } from '../../../lib/logger'; +import { dataFetcher } from '../data-fetcher'; +import type { Cart, CartRaw } from '../../../types'; import { cookiesClient } from '@/utils/server/AmplifyServer'; import { cartContextTransformer } from './cart-context-transformer'; import { cartItemTransformer } from './cart-item-transformer'; diff --git a/packages/liquid-forge/services/fetchers/cart/cart-stock-validator.ts b/packages/liquid-forge/services/fetchers/cart/cart-stock-validator.ts index a5c9e960..63b0c2b0 100644 --- a/packages/liquid-forge/services/fetchers/cart/cart-stock-validator.ts +++ b/packages/liquid-forge/services/fetchers/cart/cart-stock-validator.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { dataFetcher } from '@/liquid-forge/services/fetchers/data-fetcher'; +import { logger } from '../../../lib/logger'; +import { dataFetcher } from '../data-fetcher'; import { cookiesClient } from '@/utils/server/AmplifyServer'; export interface StockValidationResult { diff --git a/packages/liquid-forge/services/fetchers/checkout/checkout-data-transformer.ts b/packages/liquid-forge/services/fetchers/checkout/checkout-data-transformer.ts index 0194ce5e..7404747c 100644 --- a/packages/liquid-forge/services/fetchers/checkout/checkout-data-transformer.ts +++ b/packages/liquid-forge/services/fetchers/checkout/checkout-data-transformer.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { CheckoutContext, CheckoutSession } from '@/liquid-forge/types'; +import { logger } from '../../../lib/logger'; +import type { CheckoutContext, CheckoutSession } from '../../../types'; /** * Transformador de datos específico para checkout diff --git a/packages/liquid-forge/services/fetchers/checkout/checkout-fetcher.ts b/packages/liquid-forge/services/fetchers/checkout/checkout-fetcher.ts index 418c5370..90f84b74 100644 --- a/packages/liquid-forge/services/fetchers/checkout/checkout-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/checkout/checkout-fetcher.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; import type { Cart, CartSnapshot, @@ -24,7 +24,7 @@ import type { CheckoutStatus, StartCheckoutRequest, UpdateCustomerInfoRequest, -} from '@/liquid-forge/types'; +} from '../../../types'; import { checkoutDataTransformer } from './checkout-data-transformer'; import { checkoutOrderCreator } from './checkout-order-creator'; import { checkoutSessionManager } from './checkout-session-manager'; diff --git a/packages/liquid-forge/services/fetchers/checkout/checkout-order-creator.ts b/packages/liquid-forge/services/fetchers/checkout/checkout-order-creator.ts index a10a2347..d24ede9e 100644 --- a/packages/liquid-forge/services/fetchers/checkout/checkout-order-creator.ts +++ b/packages/liquid-forge/services/fetchers/checkout/checkout-order-creator.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { CheckoutResponse, CheckoutSession } from '@/liquid-forge/types'; +import { logger } from '../../../lib/logger'; +import type { CheckoutResponse, CheckoutSession } from '../../../types'; import { orderFetcher, type CreateOrderRequest } from '../order'; export class CheckoutOrderCreator { diff --git a/packages/liquid-forge/services/fetchers/checkout/checkout-session-manager.ts b/packages/liquid-forge/services/fetchers/checkout/checkout-session-manager.ts index 9a29f2f8..5ee0ece5 100644 --- a/packages/liquid-forge/services/fetchers/checkout/checkout-session-manager.ts +++ b/packages/liquid-forge/services/fetchers/checkout/checkout-session-manager.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; import { cookiesClient } from '@/utils/server/AmplifyServer'; import crypto from 'crypto'; import type { CheckoutSessionData, UserStoreCurrency } from './types/checkout-types'; diff --git a/packages/liquid-forge/services/fetchers/collection/collection-cache-manager.ts b/packages/liquid-forge/services/fetchers/collection/collection-cache-manager.ts index f300cdab..57eb347f 100644 --- a/packages/liquid-forge/services/fetchers/collection/collection-cache-manager.ts +++ b/packages/liquid-forge/services/fetchers/collection/collection-cache-manager.ts @@ -16,7 +16,7 @@ import { getCollectionPrefix, getCollectionsCacheKey, getCollectionsPrefix, -} from '@/liquid-forge/services/core/cache'; +} from '../../core/cache'; import type { CollectionContext, CollectionsResponse } from './types/collection-types'; export class CollectionCacheManager { diff --git a/packages/liquid-forge/services/fetchers/collection/collection-fetcher.ts b/packages/liquid-forge/services/fetchers/collection/collection-fetcher.ts index 5e2ea6c6..54948b05 100644 --- a/packages/liquid-forge/services/fetchers/collection/collection-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/collection/collection-fetcher.ts @@ -10,8 +10,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { CollectionContext, TemplateError } from '@/liquid-forge/types'; +import { logger } from '../../../lib/logger'; +import type { CollectionContext, TemplateError } from '../../../types'; import { productFetcher } from '../product'; import { collectionCacheManager } from './collection-cache-manager'; import { collectionQueryManager } from './collection-query-manager'; diff --git a/packages/liquid-forge/services/fetchers/collection/collection-transformer.ts b/packages/liquid-forge/services/fetchers/collection/collection-transformer.ts index a5dbd6dc..b3fc0bcf 100644 --- a/packages/liquid-forge/services/fetchers/collection/collection-transformer.ts +++ b/packages/liquid-forge/services/fetchers/collection/collection-transformer.ts @@ -10,8 +10,8 @@ * limitations under the License. */ -import { dataTransformer } from '@/liquid-forge/services/core/data-transformer'; -import type { CollectionContext, ProductContext } from '@/liquid-forge/types'; +import { dataTransformer } from '../../core/data-transformer'; +import type { CollectionContext, ProductContext } from '../../../types'; import type { CollectionData } from './types/collection-types'; export class CollectionTransformer { diff --git a/packages/liquid-forge/services/fetchers/collection/types/collection-types.ts b/packages/liquid-forge/services/fetchers/collection/types/collection-types.ts index 18640f00..659513ba 100644 --- a/packages/liquid-forge/services/fetchers/collection/types/collection-types.ts +++ b/packages/liquid-forge/services/fetchers/collection/types/collection-types.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import type { CollectionContext } from '@/liquid-forge/types'; +import type { CollectionContext } from '../../../../types'; export type { CollectionContext }; diff --git a/packages/liquid-forge/services/fetchers/data-fetcher.ts b/packages/liquid-forge/services/fetchers/data-fetcher.ts index a7651a21..b011b8c7 100644 --- a/packages/liquid-forge/services/fetchers/data-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/data-fetcher.ts @@ -14,13 +14,13 @@ * limitations under the License. */ -import { cacheManager } from '@/liquid-forge/services/core/cache'; -import { cartFetcher } from '@/liquid-forge/services/fetchers/cart'; -import { collectionFetcher } from '@/liquid-forge/services/fetchers/collection'; -import { navigationFetcher } from '@/liquid-forge/services/fetchers/navigation'; -import { pageFetcher } from '@/liquid-forge/services/fetchers/page'; -import { checkoutFetcher } from '@/liquid-forge/services/fetchers/checkout'; -import { productFetcher } from '@/liquid-forge/services/fetchers/product'; +import { cacheManager } from '../core/cache'; +import { cartFetcher } from './cart'; +import { collectionFetcher } from './collection'; +import { navigationFetcher } from './navigation'; +import { pageFetcher } from './page'; +import { checkoutFetcher } from './checkout'; +import { productFetcher } from './product'; import type { AddToCartRequest, Cart, @@ -35,7 +35,7 @@ import type { CheckoutResponse, CheckoutSession, CheckoutStatus, -} from '@/liquid-forge/types'; +} from '../../types'; interface PaginationOptions { limit?: number; diff --git a/packages/liquid-forge/services/fetchers/navigation/navigation-cache-manager.ts b/packages/liquid-forge/services/fetchers/navigation/navigation-cache-manager.ts index 53f33dbe..2a7bd8a9 100644 --- a/packages/liquid-forge/services/fetchers/navigation/navigation-cache-manager.ts +++ b/packages/liquid-forge/services/fetchers/navigation/navigation-cache-manager.ts @@ -16,7 +16,7 @@ import { getNavigationMenuCacheKey, getNavigationMenuPrefix, getNavigationPrefix, -} from '@/liquid-forge/services/core/cache'; +} from '../../core/cache'; import type { NavigationMenusResponse, ProcessedNavigationMenu } from './types/navigation-types'; export class NavigationCacheManager { diff --git a/packages/liquid-forge/services/fetchers/navigation/navigation-fetcher.ts b/packages/liquid-forge/services/fetchers/navigation/navigation-fetcher.ts index 2f41326b..c4cc8313 100644 --- a/packages/liquid-forge/services/fetchers/navigation/navigation-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/navigation/navigation-fetcher.ts @@ -10,8 +10,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { ProcessedNavigationMenu } from '@/liquid-forge/types/store'; +import { logger } from '../../../lib/logger'; +import type { ProcessedNavigationMenu } from '../../../types/store'; import { cookiesClient } from '@/utils/server/AmplifyServer'; import { navigationCacheManager } from './navigation-cache-manager'; import { navigationMenuProcessor } from './navigation-menu-processor'; diff --git a/packages/liquid-forge/services/fetchers/navigation/navigation-menu-processor.ts b/packages/liquid-forge/services/fetchers/navigation/navigation-menu-processor.ts index 21b438cc..31c0cef2 100644 --- a/packages/liquid-forge/services/fetchers/navigation/navigation-menu-processor.ts +++ b/packages/liquid-forge/services/fetchers/navigation/navigation-menu-processor.ts @@ -10,8 +10,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { NavigationMenuItem, ProcessedNavigationMenu } from '@/liquid-forge/types/store'; +import { logger } from '../../../lib/logger'; +import type { NavigationMenuItem, ProcessedNavigationMenu } from '../../../types/store'; import type { NavigationMenuData, ProcessedMenuItemData } from './types/navigation-types'; export class NavigationMenuProcessor { diff --git a/packages/liquid-forge/services/fetchers/navigation/navigation-url-resolver.ts b/packages/liquid-forge/services/fetchers/navigation/navigation-url-resolver.ts index 9bd4a97a..13982dcd 100644 --- a/packages/liquid-forge/services/fetchers/navigation/navigation-url-resolver.ts +++ b/packages/liquid-forge/services/fetchers/navigation/navigation-url-resolver.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; import { cookiesClient } from '@/utils/server/AmplifyServer'; export class NavigationUrlResolver { diff --git a/packages/liquid-forge/services/fetchers/order/order-fetcher.ts b/packages/liquid-forge/services/fetchers/order/order-fetcher.ts index a87eb42e..85d68a62 100644 --- a/packages/liquid-forge/services/fetchers/order/order-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/order/order-fetcher.ts @@ -10,16 +10,16 @@ * limitations under the License. */ -import type { Order } from '@/liquid-forge/types'; +import type { Order } from '../../../types'; import { cookiesClient } from '@/utils/server/AmplifyServer'; -import { customerInfoManager } from '@/liquid-forge/services/fetchers/order/customer-info-manager'; -import { orderItemCreator } from '@/liquid-forge/services/fetchers/order/order-item-creator'; -import { orderNumberGenerator } from '@/liquid-forge/services/fetchers/order/order-number-generator'; -import { orderValidator } from '@/liquid-forge/services/fetchers/order/order-validator'; +import { customerInfoManager } from './customer-info-manager'; +import { orderItemCreator } from './order-item-creator'; +import { orderNumberGenerator } from './order-number-generator'; +import { orderValidator } from './order-validator'; import type { CreateOrderRequest, CreateOrderResponse } from './types/order-types'; -import { EmailOrderService } from '@/liquid-forge/services/notifications/email-order-service'; -import { notificationCreator } from '@/liquid-forge/services/notifications/server'; -import { analyticsWebhookService } from '@/liquid-forge/services/analytics'; +import { EmailOrderService } from '../../notifications/email-order-service'; +import { notificationCreator } from '../../notifications/server'; +import { analyticsWebhookService } from '../../analytics'; export class OrderFetcher { /** diff --git a/packages/liquid-forge/services/fetchers/order/order-item-creator.ts b/packages/liquid-forge/services/fetchers/order/order-item-creator.ts index 56d8d86c..49c11233 100644 --- a/packages/liquid-forge/services/fetchers/order/order-item-creator.ts +++ b/packages/liquid-forge/services/fetchers/order/order-item-creator.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import type { CartSnapshot, OrderItem } from '@/liquid-forge/types'; +import type { CartSnapshot, OrderItem } from '../../../types'; import { cookiesClient } from '@/utils/server/AmplifyServer'; import type { OrderItemData, ProductSnapshotData } from './types/order-types'; diff --git a/packages/liquid-forge/services/fetchers/page/page-cache-manager.ts b/packages/liquid-forge/services/fetchers/page/page-cache-manager.ts index 76fb0e4c..f6213d01 100644 --- a/packages/liquid-forge/services/fetchers/page/page-cache-manager.ts +++ b/packages/liquid-forge/services/fetchers/page/page-cache-manager.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import { cacheManager, getPageCacheKey, getPagesCacheKey, getPagesPrefix } from '@/liquid-forge/services/core/cache'; +import { cacheManager, getPageCacheKey, getPagesCacheKey, getPagesPrefix } from '../../core/cache'; import type { PageContext, PagesResponse } from './types/page-types'; export class PageCacheManager { diff --git a/packages/liquid-forge/services/fetchers/page/page-fetcher.ts b/packages/liquid-forge/services/fetchers/page/page-fetcher.ts index 8ba44213..506ca65d 100644 --- a/packages/liquid-forge/services/fetchers/page/page-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/page/page-fetcher.ts @@ -10,8 +10,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { PageContext, TemplateError } from '@/liquid-forge/types'; +import { logger } from '../../../lib/logger'; +import type { PageContext, TemplateError } from '../../../types'; import { pageCacheManager } from './page-cache-manager'; import { pageQueryManager } from './page-query-manager'; import { pageTransformer } from './page-transformer'; diff --git a/packages/liquid-forge/services/fetchers/page/page-transformer.ts b/packages/liquid-forge/services/fetchers/page/page-transformer.ts index e59d1be5..c0ac5724 100644 --- a/packages/liquid-forge/services/fetchers/page/page-transformer.ts +++ b/packages/liquid-forge/services/fetchers/page/page-transformer.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import { dataTransformer } from '@/liquid-forge/services/core/data-transformer'; +import { dataTransformer } from '../../core/data-transformer'; import type { PageContext, PageData } from './types/page-types'; export class PageTransformer { diff --git a/packages/liquid-forge/services/fetchers/page/types/page-types.ts b/packages/liquid-forge/services/fetchers/page/types/page-types.ts index d5e0203f..797814c3 100644 --- a/packages/liquid-forge/services/fetchers/page/types/page-types.ts +++ b/packages/liquid-forge/services/fetchers/page/types/page-types.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import type { PageContext } from '@/liquid-forge/types'; +import type { PageContext } from '../../../../types'; export type { PageContext }; diff --git a/packages/liquid-forge/services/fetchers/product/product-cache-manager.ts b/packages/liquid-forge/services/fetchers/product/product-cache-manager.ts index c4eebb3d..6d0ade53 100644 --- a/packages/liquid-forge/services/fetchers/product/product-cache-manager.ts +++ b/packages/liquid-forge/services/fetchers/product/product-cache-manager.ts @@ -18,7 +18,7 @@ import { getProductHandleMapCacheKey, getProductsCacheKey, getProductsPrefix, -} from '@/liquid-forge/services/core/cache'; +} from '../../core/cache'; import type { ProductContext, ProductsResponse } from './types/product-types'; export class ProductCacheManager { diff --git a/packages/liquid-forge/services/fetchers/product/product-fetcher.ts b/packages/liquid-forge/services/fetchers/product/product-fetcher.ts index c27c35cb..304ac92d 100644 --- a/packages/liquid-forge/services/fetchers/product/product-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/product/product-fetcher.ts @@ -10,8 +10,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { ProductContext, TemplateError } from '@/liquid-forge/types'; +import { logger } from '../../../lib/logger'; +import type { ProductContext, TemplateError } from '../../../types'; import { productCacheManager } from './product-cache-manager'; import { productHandleManager } from './product-handle-manager'; import { productQueryManager } from './product-query-manager'; diff --git a/packages/liquid-forge/services/fetchers/product/product-handle-manager.ts b/packages/liquid-forge/services/fetchers/product/product-handle-manager.ts index c5ad7356..0aaa80ba 100644 --- a/packages/liquid-forge/services/fetchers/product/product-handle-manager.ts +++ b/packages/liquid-forge/services/fetchers/product/product-handle-manager.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import { dataTransformer } from '@/liquid-forge/services/core/data-transformer'; +import { dataTransformer } from '../../core/data-transformer'; import { productCacheManager } from './product-cache-manager'; import { productQueryManager } from './product-query-manager'; import type { ProductData, ProductHandleMap } from './types/product-types'; diff --git a/packages/liquid-forge/services/fetchers/product/product-transformer.ts b/packages/liquid-forge/services/fetchers/product/product-transformer.ts index 6154bc53..d41e890c 100644 --- a/packages/liquid-forge/services/fetchers/product/product-transformer.ts +++ b/packages/liquid-forge/services/fetchers/product/product-transformer.ts @@ -10,8 +10,8 @@ * limitations under the License. */ -import { dataTransformer } from '@/liquid-forge/services/core/data-transformer'; -import type { ProductAttribute, ProductContext } from '@/liquid-forge/types'; +import { dataTransformer } from '../../core/data-transformer'; +import type { ProductAttribute, ProductContext } from '../../../types'; import type { ProductData } from './types/product-types'; export class ProductTransformer { diff --git a/packages/liquid-forge/services/fetchers/product/types/product-types.ts b/packages/liquid-forge/services/fetchers/product/types/product-types.ts index 786fbf43..26a46f01 100644 --- a/packages/liquid-forge/services/fetchers/product/types/product-types.ts +++ b/packages/liquid-forge/services/fetchers/product/types/product-types.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import type { ProductContext } from '@/liquid-forge/types'; +import type { ProductContext } from '../../../../types'; export type { ProductContext }; diff --git a/packages/liquid-forge/services/notifications/email-notification-service.ts b/packages/liquid-forge/services/notifications/email-notification-service.ts index 2180a907..c1c81b3a 100644 --- a/packages/liquid-forge/services/notifications/email-notification-service.ts +++ b/packages/liquid-forge/services/notifications/email-notification-service.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import type { Order } from '@/liquid-forge/types'; +import type { Order } from '../../types'; import { getOrderStatus, getPaymentStatus } from './status-translations'; import { EmailFormattingUtils, type Address, type AddressInfo } from './email-formatting-utils'; import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda'; diff --git a/packages/liquid-forge/services/notifications/email-order-service.ts b/packages/liquid-forge/services/notifications/email-order-service.ts index 9246eee8..50f8dbe9 100644 --- a/packages/liquid-forge/services/notifications/email-order-service.ts +++ b/packages/liquid-forge/services/notifications/email-order-service.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import type { Order } from '@/liquid-forge/types'; +import type { Order } from '../../types'; import { cookiesClient } from '@/utils/server/AmplifyServer'; import { EmailNotificationService, EmailFormattingUtils, type Address } from './index'; diff --git a/packages/liquid-forge/services/notifications/notification-creator.ts b/packages/liquid-forge/services/notifications/notification-creator.ts index e502535a..c88c495c 100644 --- a/packages/liquid-forge/services/notifications/notification-creator.ts +++ b/packages/liquid-forge/services/notifications/notification-creator.ts @@ -15,8 +15,8 @@ */ import { cookiesClient } from '@/utils/server/AmplifyServer'; -import type { Order } from '@/liquid-forge/types'; -import { logger } from '@/liquid-forge/lib/logger'; +import type { Order } from '../../types'; +import { logger } from '../../lib/logger'; import type { CreateAdminNotificationRequest } from './types/notification-types'; import { EmailFormattingUtils } from './email-formatting-utils'; diff --git a/packages/liquid-forge/services/notifications/types/notification-types.ts b/packages/liquid-forge/services/notifications/types/notification-types.ts index 632c6512..b2fbd8ac 100644 --- a/packages/liquid-forge/services/notifications/types/notification-types.ts +++ b/packages/liquid-forge/services/notifications/types/notification-types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { Order } from '@/liquid-forge/types'; +import type { Order } from '../../../types'; export interface CreateAdminNotificationRequest { order: Order; diff --git a/packages/liquid-forge/services/page/data-loader/core/context-builder-helper.ts b/packages/liquid-forge/services/page/data-loader/core/context-builder-helper.ts index 0f17f7f2..474b8fd2 100644 --- a/packages/liquid-forge/services/page/data-loader/core/context-builder-helper.ts +++ b/packages/liquid-forge/services/page/data-loader/core/context-builder-helper.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import type { PageRenderOptions } from '../../../../types/template'; /** * Tipo para builders de contexto por página diff --git a/packages/liquid-forge/services/page/data-loader/core/core-data-loader.ts b/packages/liquid-forge/services/page/data-loader/core/core-data-loader.ts index e8fe765d..a7e727df 100644 --- a/packages/liquid-forge/services/page/data-loader/core/core-data-loader.ts +++ b/packages/liquid-forge/services/page/data-loader/core/core-data-loader.ts @@ -14,13 +14,13 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { loadDataFromAnalysis } from '@/liquid-forge/services/page/data-loader'; -import { buildContextData } from '@/liquid-forge/services/page/data-loader/core/context-builder-helper'; -import { analyzeRequiredTemplates } from '@/liquid-forge/services/page/data-loader/core/template-analyzer-helper'; -import { buildPaginationObject } from '@/liquid-forge/services/page/data-loader/pagination/pagination-builder-helper'; -import type { TemplateAnalysis } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import type { PageRenderOptions, PaginationInfo } from '@/liquid-forge/types/template'; +import { logger } from '../../../../lib/logger'; +import { loadDataFromAnalysis } from '..'; +import { buildContextData } from './context-builder-helper'; +import { analyzeRequiredTemplates } from './template-analyzer-helper'; +import { buildPaginationObject } from '../pagination/pagination-builder-helper'; +import type { TemplateAnalysis } from '../../../templates/analysis/template-analyzer'; +import type { PageRenderOptions, PaginationInfo } from '../../../../types/template'; /** * Interfaz para los datos principales cargados diff --git a/packages/liquid-forge/services/page/data-loader/core/template-analyzer-helper.ts b/packages/liquid-forge/services/page/data-loader/core/template-analyzer-helper.ts index c96ab87c..a8d7abac 100644 --- a/packages/liquid-forge/services/page/data-loader/core/template-analyzer-helper.ts +++ b/packages/liquid-forge/services/page/data-loader/core/template-analyzer-helper.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { extractPaginationLimitFromTemplate } from '@/liquid-forge/services/page/data-loader/pagination/pagination-limit-extractor'; -import type { TemplateAnalysis } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import { templateAnalyzer } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import { templateLoader } from '@/liquid-forge/services/templates/template-loader'; -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import { logger } from '../../../../lib/logger'; +import { extractPaginationLimitFromTemplate } from '../pagination/pagination-limit-extractor'; +import type { TemplateAnalysis } from '../../../templates/analysis/template-analyzer'; +import { templateAnalyzer } from '../../../templates/analysis/template-analyzer'; +import { templateLoader } from '../../../templates/template-loader'; +import type { PageRenderOptions } from '../../../../types/template'; /** * Tipo para cargadores de templates diff --git a/packages/liquid-forge/services/page/data-loader/data-fetcher-helper.ts b/packages/liquid-forge/services/page/data-loader/data-fetcher-helper.ts index c0a79e45..35b3494d 100644 --- a/packages/liquid-forge/services/page/data-loader/data-fetcher-helper.ts +++ b/packages/liquid-forge/services/page/data-loader/data-fetcher-helper.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { TemplateAnalysis } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import type { PageRenderOptions, PaginationInfo } from '@/liquid-forge/types/template'; +import { logger } from '../../../lib/logger'; +import type { TemplateAnalysis } from '../../templates/analysis/template-analyzer'; +import type { PageRenderOptions, PaginationInfo } from '../../../types/template'; import { loadSpecificData } from './handlers/data-handlers'; import { responseProcessors } from './handlers/response-processors'; diff --git a/packages/liquid-forge/services/page/data-loader/handlers/data-handlers.ts b/packages/liquid-forge/services/page/data-loader/handlers/data-handlers.ts index c5183ba2..1e147c05 100644 --- a/packages/liquid-forge/services/page/data-loader/handlers/data-handlers.ts +++ b/packages/liquid-forge/services/page/data-loader/handlers/data-handlers.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { checkoutFetcher } from '@/liquid-forge/services/fetchers/checkout'; -import { dataFetcher } from '@/liquid-forge/services/fetchers/data-fetcher'; -import type { DataLoadOptions, DataRequirement } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import { logger } from '../../../../lib/logger'; +import { checkoutFetcher } from '../../../fetchers/checkout'; +import { dataFetcher } from '../../../fetchers/data-fetcher'; +import type { DataLoadOptions, DataRequirement } from '../../../templates/analysis/template-analyzer'; +import type { PageRenderOptions } from '../../../../types/template'; /** * Tipo para las funciones handlers de datos diff --git a/packages/liquid-forge/services/page/data-loader/handlers/response-processors.ts b/packages/liquid-forge/services/page/data-loader/handlers/response-processors.ts index 834d94df..b7d95484 100644 --- a/packages/liquid-forge/services/page/data-loader/handlers/response-processors.ts +++ b/packages/liquid-forge/services/page/data-loader/handlers/response-processors.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import type { DataRequirement } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import type { PaginationInfo } from '@/liquid-forge/types/template'; +import type { DataRequirement } from '../../../templates/analysis/template-analyzer'; +import type { PaginationInfo } from '../../../../types/template'; /** * Tipo para procesadores de respuesta de datos diff --git a/packages/liquid-forge/services/page/data-loader/pagination/pagination-builder-helper.ts b/packages/liquid-forge/services/page/data-loader/pagination/pagination-builder-helper.ts index c28fb541..8f75439a 100644 --- a/packages/liquid-forge/services/page/data-loader/pagination/pagination-builder-helper.ts +++ b/packages/liquid-forge/services/page/data-loader/pagination/pagination-builder-helper.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import type { TemplateAnalysis } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import type { PaginationInfo } from '@/liquid-forge/types/template'; +import type { TemplateAnalysis } from '../../../templates/analysis/template-analyzer'; +import type { PaginationInfo } from '../../../../types/template'; interface LoadedData { products?: any[]; diff --git a/packages/liquid-forge/services/page/data-loader/pagination/pagination-limit-extractor.ts b/packages/liquid-forge/services/page/data-loader/pagination/pagination-limit-extractor.ts index 783338bc..959c972a 100644 --- a/packages/liquid-forge/services/page/data-loader/pagination/pagination-limit-extractor.ts +++ b/packages/liquid-forge/services/page/data-loader/pagination/pagination-limit-extractor.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { DataRequirement, TemplateAnalysis } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import { logger } from '../../../../lib/logger'; +import type { DataRequirement, TemplateAnalysis } from '../../../templates/analysis/template-analyzer'; +import type { PageRenderOptions } from '../../../../types/template'; /** * Mapeo declarativo de tipos de página a paths de templates. diff --git a/packages/liquid-forge/services/page/data-loader/search/search-data-loader.ts b/packages/liquid-forge/services/page/data-loader/search/search-data-loader.ts index b797057f..a0ef63e2 100644 --- a/packages/liquid-forge/services/page/data-loader/search/search-data-loader.ts +++ b/packages/liquid-forge/services/page/data-loader/search/search-data-loader.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { dataFetcher } from '@/liquid-forge/services/fetchers/data-fetcher'; -import { productTransformer } from '@/liquid-forge/services/fetchers/product'; -import { extractSearchLimitsFromSettings } from '@/liquid-forge/services/page/data-loader/search/search-limits-extractor'; -import type { ProductContext } from '@/liquid-forge/types'; +import { logger } from '../../../../lib/logger'; +import { dataFetcher } from '../../../fetchers/data-fetcher'; +import { productTransformer } from '../../../fetchers/product'; +import { extractSearchLimitsFromSettings } from './search-limits-extractor'; +import type { ProductContext } from '../../../../types'; import { cookiesClient } from '@/utils/server/AmplifyServer'; /** diff --git a/packages/liquid-forge/services/page/data-loader/search/search-limits-extractor.ts b/packages/liquid-forge/services/page/data-loader/search/search-limits-extractor.ts index dbd81fe6..dcee85ac 100644 --- a/packages/liquid-forge/services/page/data-loader/search/search-limits-extractor.ts +++ b/packages/liquid-forge/services/page/data-loader/search/search-limits-extractor.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../../lib/logger'; /** * Extrae los límites de búsqueda del archivo settings_schema.json diff --git a/packages/liquid-forge/services/page/dynamic-data-loader.ts b/packages/liquid-forge/services/page/dynamic-data-loader.ts index 99f20b03..26807a72 100644 --- a/packages/liquid-forge/services/page/dynamic-data-loader.ts +++ b/packages/liquid-forge/services/page/dynamic-data-loader.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { coreDataLoader, searchDataLoader } from '@/liquid-forge/services/page/data-loader'; -import type { CoreData } from '@/liquid-forge/services/page/data-loader/core/core-data-loader'; -import type { SearchData } from '@/liquid-forge/services/page/data-loader/search/search-data-loader'; -import type { CartContext } from '@/liquid-forge/types'; -import type { PageRenderOptions, PaginationInfo } from '@/liquid-forge/types/template'; +import { logger } from '../../lib/logger'; +import { coreDataLoader, searchDataLoader } from './data-loader'; +import type { CoreData } from './data-loader/core/core-data-loader'; +import type { SearchData } from './data-loader/search/search-data-loader'; +import type { CartContext } from '../../types'; +import type { PageRenderOptions, PaginationInfo } from '../../types/template'; /** * Resultado de la carga dinámica de datos diff --git a/packages/liquid-forge/services/rendering/global-context.ts b/packages/liquid-forge/services/rendering/global-context.ts index e45628c6..e80fcc94 100644 --- a/packages/liquid-forge/services/rendering/global-context.ts +++ b/packages/liquid-forge/services/rendering/global-context.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { flexibleLinkListService, linkListService } from '@/liquid-forge/services/core/navigation-service'; -import type { CartContext, PageContext, RenderContext, ShopContext } from '@/liquid-forge/types'; +import { logger } from '../../lib/logger'; +import { flexibleLinkListService, linkListService } from '../core/navigation-service'; +import type { CartContext, PageContext, RenderContext, ShopContext } from '../../types'; export class ContextBuilder { /** diff --git a/packages/liquid-forge/services/rendering/metadata-generator.ts b/packages/liquid-forge/services/rendering/metadata-generator.ts index 7c1afeab..564e665c 100644 --- a/packages/liquid-forge/services/rendering/metadata-generator.ts +++ b/packages/liquid-forge/services/rendering/metadata-generator.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { OpenGraphData, RenderResult, SchemaData, TwitterCardData } from '@/liquid-forge/types/template'; +import type { OpenGraphData, RenderResult, SchemaData, TwitterCardData } from '../../types/template'; export class MetadataGenerator { /** diff --git a/packages/liquid-forge/services/rendering/page-html-cache.ts b/packages/liquid-forge/services/rendering/page-html-cache.ts index 07ddf604..b1e0f868 100644 --- a/packages/liquid-forge/services/rendering/page-html-cache.ts +++ b/packages/liquid-forge/services/rendering/page-html-cache.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { getPageCacheKey } from '@/liquid-forge/services/core/cache/cache-keys'; -import { cacheManager } from '@/liquid-forge/services/core/cache'; -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import { getPageCacheKey } from '../core/cache/cache-keys'; +import { cacheManager } from '../core/cache'; +import type { PageRenderOptions } from '../../types/template'; export interface CachedPageRender { html: string; diff --git a/packages/liquid-forge/services/rendering/render-page-content.ts b/packages/liquid-forge/services/rendering/render-page-content.ts index b59b9d29..c72e3633 100644 --- a/packages/liquid-forge/services/rendering/render-page-content.ts +++ b/packages/liquid-forge/services/rendering/render-page-content.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { liquidEngine } from '@/liquid-forge/liquid/engine'; -import { sectionRenderer } from '@/liquid-forge/services/rendering/section-renderer'; -import { templateLoader } from '@/liquid-forge/services/templates/template-loader'; -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import { logger } from '../../lib/logger'; +import { liquidEngine } from '../../liquid/engine'; +import { sectionRenderer } from './section-renderer'; +import { templateLoader } from '../templates/template-loader'; +import type { PageRenderOptions } from '../../types/template'; import type { Template } from 'liquidjs'; /** diff --git a/packages/liquid-forge/services/rendering/section-renderer.ts b/packages/liquid-forge/services/rendering/section-renderer.ts index 9a3037ae..a2dbfd1d 100644 --- a/packages/liquid-forge/services/rendering/section-renderer.ts +++ b/packages/liquid-forge/services/rendering/section-renderer.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { liquidEngine } from '@/liquid-forge/liquid/engine'; -import { schemaParser } from '@/liquid-forge/services/templates/parsing/schema-parser'; -import { templateLoader } from '@/liquid-forge/services/templates/template-loader'; -import type { RenderContext } from '@/liquid-forge/types'; +import { logger } from '../../lib/logger'; +import { liquidEngine } from '../../liquid/engine'; +import { schemaParser } from '../templates/parsing/schema-parser'; +import { templateLoader } from '../templates/template-loader'; +import type { RenderContext } from '../../types'; export class SectionRenderer { /** diff --git a/packages/liquid-forge/services/templates/analysis/template-analyzer.ts b/packages/liquid-forge/services/templates/analysis/template-analyzer.ts index 2bf79de2..321cbc08 100644 --- a/packages/liquid-forge/services/templates/analysis/template-analyzer.ts +++ b/packages/liquid-forge/services/templates/analysis/template-analyzer.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { LiquidSyntaxDetector } from '@/liquid-forge/services/templates/parsing/liquid-syntax-detector'; +import { logger } from '../../../lib/logger'; +import { LiquidSyntaxDetector } from '../parsing/liquid-syntax-detector'; /** * Tipo de datos que pueden ser detectados en una plantilla @@ -116,7 +116,7 @@ export class TemplateAnalyzer { storeId: string, initialTemplates: { [path: string]: string } ): Promise { - const { templateLoader } = await import('@/liquid-forge/services/templates/template-loader'); + const { templateLoader } = await import('../template-loader'); const combinedAnalysis: TemplateAnalysis = { requiredData: new Map(), diff --git a/packages/liquid-forge/services/templates/parsing/liquid-syntax-detector.ts b/packages/liquid-forge/services/templates/parsing/liquid-syntax-detector.ts index 13e36999..e1420342 100644 --- a/packages/liquid-forge/services/templates/parsing/liquid-syntax-detector.ts +++ b/packages/liquid-forge/services/templates/parsing/liquid-syntax-detector.ts @@ -14,11 +14,7 @@ * limitations under the License. */ -import type { - DataLoadOptions, - DataRequirement, - TemplateAnalysis, -} from '@/liquid-forge/services/templates/analysis/template-analyzer'; +import type { DataLoadOptions, DataRequirement, TemplateAnalysis } from '../analysis/template-analyzer'; /** * Tipo para detectores de objetos Liquid diff --git a/packages/liquid-forge/services/templates/sync/template-dev-synchronizer.ts b/packages/liquid-forge/services/templates/sync/template-dev-synchronizer.ts index 3c463edf..77e6a90e 100644 --- a/packages/liquid-forge/services/templates/sync/template-dev-synchronizer.ts +++ b/packages/liquid-forge/services/templates/sync/template-dev-synchronizer.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { cacheManager } from '@/liquid-forge/services/core/cache'; -import { cacheInvalidationService } from '@/liquid-forge/services/core/cache/cache-invalidation-service'; -import { templateLoader } from '@/liquid-forge/services/templates/template-loader'; -import { PostCSSProcessor } from '@/liquid-forge/services/themes/optimization/postcss-processor'; +import { logger } from '../../../lib/logger'; +import { cacheManager } from '../../core/cache'; +import { cacheInvalidationService } from '../../core/cache/cache-invalidation-service'; +import { templateLoader } from '../template-loader'; +import { PostCSSProcessor } from '../../themes/optimization/postcss-processor'; import { getContentType, isBinaryFile } from '@/lib/utils/file-utils'; import { DeleteObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3'; import { Upload } from '@aws-sdk/lib-storage'; diff --git a/packages/liquid-forge/services/templates/template-loader.ts b/packages/liquid-forge/services/templates/template-loader.ts index 2c6595ec..4b79727d 100644 --- a/packages/liquid-forge/services/templates/template-loader.ts +++ b/packages/liquid-forge/services/templates/template-loader.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { liquidEngine } from '@/liquid-forge/liquid/engine'; +import { liquidEngine } from '../../liquid/engine'; import { cacheManager, getAssetCacheKey, @@ -22,8 +22,8 @@ import { getCompiledTemplatesPrefix, getTemplateCacheKey, getTemplatesPrefix, -} from '@/liquid-forge/services/core/cache'; -import type { TemplateCache, TemplateError } from '@/liquid-forge/types'; +} from '../core/cache'; +import type { TemplateCache, TemplateError } from '../../types'; import { getCdnUrlForKey } from '@/utils/server'; import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3'; import type { Template } from 'liquidjs'; diff --git a/packages/liquid-forge/services/themes/core/theme-processor.ts b/packages/liquid-forge/services/themes/core/theme-processor.ts index eef69795..2821f660 100644 --- a/packages/liquid-forge/services/themes/core/theme-processor.ts +++ b/packages/liquid-forge/services/themes/core/theme-processor.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { TemplateAnalysis } from '@/liquid-forge/exports'; -import { RendererLogger } from '@/liquid-forge/lib/logger'; -import { SchemaParser } from '@/liquid-forge/services/templates/parsing/schema-parser'; +import { TemplateAnalysis } from '../../../exports'; +import { RendererLogger } from '../../../lib/logger'; +import { SchemaParser } from '../../templates/parsing/schema-parser'; import { PostCSSProcessor } from '../optimization/postcss-processor'; import JSZip from 'jszip'; import type { diff --git a/packages/liquid-forge/services/themes/storage/s3-storage-service.ts b/packages/liquid-forge/services/themes/storage/s3-storage-service.ts index a951d839..140dc440 100644 --- a/packages/liquid-forge/services/themes/storage/s3-storage-service.ts +++ b/packages/liquid-forge/services/themes/storage/s3-storage-service.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { RendererLogger } from '@/liquid-forge/lib/logger'; +import { RendererLogger } from '../../../lib/logger'; import { getCdnUrlForKey } from '@/utils/server'; import { getContentType } from '@/lib/utils'; import { diff --git a/packages/liquid-forge/services/themes/validation/rules/liquid-syntax-rules.ts b/packages/liquid-forge/services/themes/validation/rules/liquid-syntax-rules.ts index 6520e4a3..d9aee66c 100644 --- a/packages/liquid-forge/services/themes/validation/rules/liquid-syntax-rules.ts +++ b/packages/liquid-forge/services/themes/validation/rules/liquid-syntax-rules.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { LiquidEngine } from '@/liquid-forge/liquid/engine'; +import { LiquidEngine } from '../../../../liquid/engine'; import type { ThemeFile, ThemeValidationConfig, diff --git a/packages/liquid-forge/services/themes/validation/validation-engine.ts b/packages/liquid-forge/services/themes/validation/validation-engine.ts index 3ea19d0a..4c8a4dd1 100644 --- a/packages/liquid-forge/services/themes/validation/validation-engine.ts +++ b/packages/liquid-forge/services/themes/validation/validation-engine.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { RendererLogger } from '@/liquid-forge/lib/logger'; +import { RendererLogger } from '../../../lib/logger'; import type { ThemeFile, ThemeValidationConfig, ValidationResult } from '../types'; import { FileStructureRules } from './rules/file-structure-rules'; import { LiquidSyntaxRules } from './rules/liquid-syntax-rules'; diff --git a/packages/liquid-forge/types/template.ts b/packages/liquid-forge/types/template.ts index 98e36bee..2f0afd54 100644 --- a/packages/liquid-forge/types/template.ts +++ b/packages/liquid-forge/types/template.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { AssetCollector } from '@/liquid-forge/services/rendering/asset-collector'; +import type { AssetCollector } from '../services/rendering/asset-collector'; export interface TemplateFile { path: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 74bcba1b..25f4c2c7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -413,6 +413,9 @@ importers: jest: specifier: ^30.0.4 version: 30.2.0(@types/node@24.6.2)(ts-node@10.9.2(@types/node@24.6.2)(typescript@5.9.3)) + tsx: + specifier: ^4.20.6 + version: 4.20.6 typescript: specifier: ^5.8.3 version: 5.9.3 @@ -10843,7 +10846,7 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.621.0(@aws-sdk/client-sts@3.621.0) + '@aws-sdk/client-sso-oidc': 3.621.0(@aws-sdk/client-sts@3.901.0) '@aws-sdk/client-sts': 3.621.0 '@aws-sdk/core': 3.621.0 '@aws-sdk/credential-provider-node': 3.621.0(@aws-sdk/client-sso-oidc@3.621.0(@aws-sdk/client-sts@3.621.0))(@aws-sdk/client-sts@3.621.0) @@ -10981,7 +10984,7 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.621.0(@aws-sdk/client-sts@3.901.0) + '@aws-sdk/client-sso-oidc': 3.621.0(@aws-sdk/client-sts@3.621.0) '@aws-sdk/client-sts': 3.621.0 '@aws-sdk/core': 3.621.0 '@aws-sdk/credential-provider-node': 3.621.0(@aws-sdk/client-sso-oidc@3.621.0(@aws-sdk/client-sts@3.621.0))(@aws-sdk/client-sts@3.621.0) From 6d3826d0a043cda92c498536e9a5f160ac662d01 Mon Sep 17 00:00:00 2001 From: Steven Date: Sun, 26 Oct 2025 21:19:04 -0500 Subject: [PATCH 7/7] Refactor filters and tags to utilize regex patterns for improved maintainability This commit updates various filters and tags within the liquid-forge package to replace hardcoded regex patterns with centralized pattern definitions from the regex-patterns module. This change enhances code readability and maintainability by consolidating pattern management. Additionally, it modifies the import paths in several files for better organization and clarity, ensuring a more streamlined codebase. --- next-env.d.ts | 2 +- .../liquid-forge/lib/regex-patterns/README.md | 305 ++++++++++++++++++ .../lib/regex-patterns/filters.ts | 40 +++ .../liquid-forge/lib/regex-patterns/index.ts | 21 ++ .../lib/regex-patterns/liquid-syntax.ts | 79 +++++ .../lib/regex-patterns/minification.ts | 41 +++ .../lib/regex-patterns/utility.ts | 32 ++ .../lib/regex-patterns/validation.ts | 51 +++ .../liquid/filters/base-filters.ts | 31 +- .../liquid/filters/ecommerce-filters.ts | 3 +- .../liquid/filters/html-filters.ts | 5 +- .../liquid/tags/core/render-tag.ts | 3 +- .../liquid/tags/data/javascript-tag.ts | 19 +- .../liquid/tags/styling/style-tag.ts | 23 +- .../services/core/data-transformer.ts | 25 +- .../parsing/liquid-syntax-detector.ts | 123 ++++--- .../templates/parsing/schema-parser.ts | 14 +- .../sync/template-dev-synchronizer.ts | 14 +- .../services/themes/core/theme-processor.ts | 3 +- .../themes/optimization/postcss-processor.ts | 11 +- .../validation/rules/performance-rules.ts | 8 +- .../themes/validation/rules/security-rules.ts | 39 ++- test/unit/liquid-tags/render-tag.test.ts | 9 +- 23 files changed, 733 insertions(+), 168 deletions(-) create mode 100644 packages/liquid-forge/lib/regex-patterns/README.md create mode 100644 packages/liquid-forge/lib/regex-patterns/filters.ts create mode 100644 packages/liquid-forge/lib/regex-patterns/index.ts create mode 100644 packages/liquid-forge/lib/regex-patterns/liquid-syntax.ts create mode 100644 packages/liquid-forge/lib/regex-patterns/minification.ts create mode 100644 packages/liquid-forge/lib/regex-patterns/utility.ts create mode 100644 packages/liquid-forge/lib/regex-patterns/validation.ts diff --git a/next-env.d.ts b/next-env.d.ts index 9edff1c7..c4b7818f 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/packages/liquid-forge/lib/regex-patterns/README.md b/packages/liquid-forge/lib/regex-patterns/README.md new file mode 100644 index 00000000..cd892674 --- /dev/null +++ b/packages/liquid-forge/lib/regex-patterns/README.md @@ -0,0 +1,305 @@ +# Documentación de Patrones Regex + +Este directorio contiene todos los patrones de expresiones regulares centralizados utilizados en todo el paquete `liquid-forge`. Todos los patrones están organizados por categoría y exportados a través del archivo principal `index.ts`. + +## Estructura del Directorio + +``` +lib/regex-patterns/ +├── index.ts # Archivo de exportación principal +├── liquid-syntax.ts # Patrones de sintaxis Liquid +├── minification.ts # Patrones de minificación +├── filters.ts # Patrones de filtros +├── validation.ts # Patrones de validación y seguridad +├── utility.ts # Patrones de utilidad +└── README.md # Este archivo +``` + +## Categorías de Patrones + +### 1. Sintaxis Liquid (`liquid-syntax.ts`) + +Patrones para detectar y analizar la sintaxis de plantillas Liquid. + +#### LIQUID_OBJECT_PATTERNS + +Detecta objetos Liquid en plantillas: + +- `products`: Detecta `{{ products |` +- `collections`: Detecta `{{ collections |` +- `product`: Detecta `{{ product.` +- `collection`: Detecta `{{ collection.` +- `linklists`: Detecta `{{ linklists.` +- `shop`: Detecta `{{ shop.` +- `pages`: Detecta `{{ pages |` +- `page`: Detecta `{{ page.` +- `blog`: Detecta `{{ blog.` +- `checkout`: Detecta `{{ checkout.` +- `relatedProducts`: Detecta uso de productos relacionados +- `pagination`: Detecta `{% paginate` + +#### LIQUID_TAG_PATTERNS + +Detecta etiquetas Liquid: + +- `paginate`: Detecta etiquetas de paginación +- `section`: Detecta etiquetas de sección +- `render`: Detecta etiquetas de render +- `include`: Detecta etiquetas include + +#### LIQUID_COLLECTION_ACCESS_PATTERNS + +Patrones para acceder a colecciones y páginas: + +- `bracketNotation`: `collections['handle']` +- `dotNotation`: `collections.handle` +- `specificAccess`: Combina notación de corchetes y punto +- `collectionProducts`: `collections.handle.products` +- `specificPage`: Acceso a páginas específicas +- `specificProduct`: Acceso a productos específicos +- `extractCollectionHandle`: Extraer handle de colección del match +- `extractPagesBracket`: Buscar notación de corchetes de páginas +- `extractPagesDot`: Buscar notación de punto de páginas +- `extractPagesHandle`: Extraer handle de página +- `pagesBracketExtract`: Extraer handle de notación de corchetes + +#### LIQUID_FILTER_PATTERNS + +Patrones para análisis de filtros: + +- `productRelated`: Extraer límite de productos relacionados +- `productsLimit`: Extraer límite de productos +- `collectionsLimit`: Extraer límite de colecciones +- `generalLimit`: Extraer límite general +- `paginateBy`: Dividir cláusula de paginación + +#### LIQUID_OPTION_EXTRACTOR_PATTERNS + +Patrones para extraer opciones: + +- `sectionName`: Extraer nombre de sección +- `snippetName`: Extraer nombre de snippet +- `collectionHandle`: Extraer handle de colección + +#### LIQUID_PAGINATION_PATTERNS + +Patrones para paginación: + +- `paginate`: Etiqueta principal de paginación +- `policies`: Detectar bucles de políticas +- `pagesLimit`: Extraer límite de páginas + +#### LIQUID_VARIABLE_PATTERNS + +Patrones para variables: + +- `variable`: Coincidir variables Liquid +- `cleanVariableTags`: Limpiar etiquetas de variables + +--- + +### 2. Minificación (`minification.ts`) + +Patrones para minificación y optimización de código. + +#### MINIFICATION_PATTERNS + +Patrones generales de minificación: + +- `htmlComment`: Eliminar comentarios HTML +- `blockComment`: Eliminar comentarios de bloque `/* */` +- `lineComment`: Eliminar comentarios de línea `//` +- `trimLines`: Eliminar espacios iniciales y finales +- `multipleNewlines`: Eliminar líneas vacías múltiples +- `multipleSpaces`: Colapsar espacios múltiples +- `operatorSpaces`: Limpiar espacios de operadores +- `liquidComment`: Eliminar comentarios Liquid + +#### CSS_MINIFICATION_PATTERNS + +Patrones específicos de CSS: + +- `comments`: Eliminar comentarios CSS +- `spaces`: Colapsar espacios +- `syntax`: Limpiar espacios alrededor de sintaxis + +#### CSS_OPTIMIZATION_PATTERNS + +Optimización CSS: + +- `comments`: Eliminar comentarios +- `braces`: Normalizar espacios alrededor de llaves +- `semicolons`: Normalizar espacios alrededor de punto y coma +- `emptyLines`: Eliminar líneas vacías + +--- + +### 3. Filtros (`filters.ts`) + +Patrones utilizados por los filtros Liquid. + +#### ESCAPE_PATTERNS + +Escapado HTML: + +- `ampersand`: Escapar `&` a `&` +- `lessThan`: Escapar `<` a `<` +- `greaterThan`: Escapar `>` a `>` +- `doubleQuote`: Escapar `"` a `"` +- `apostrophe`: Escapar `'` a `'` + +#### HANDLE_PATTERNS + +Creación de handles (slugs amigables para SEO): + +- `aVariants`: Normalizar á, à, ä, â, ã a 'a' +- `eVariants`: Normalizar é, è, ë, ê a 'e' +- `iVariants`: Normalizar í, ì, ï, î a 'i' +- `oVariants`: Normalizar ó, ò, ö, ô, õ a 'o' +- `uVariants`: Normalizar ú, ù, ü, û a 'u' +- `enye`: Normalizar ñ a 'n' +- `cCedilla`: Normalizar ç a 'c' +- `nonAlphanumeric`: Reemplazar no alfanuméricos con `-` +- `multipleDashes`: Colapsar guiones múltiples +- `leadingTrailingDash`: Eliminar guiones iniciales/finales + +#### URL_PATTERNS + +Manipulación de URLs: + +- `url`: Coincidir URLs +- `urlProtocol`: Extraer protocolo +- `urlDomain`: Extraer dominio +- `urlPath`: Extraer ruta +- `urlQuery`: Extraer cadena de consulta +- `urlHash`: Extraer hash +- `isAbsolute`: Verificar si URL es absoluta +- `makeAbsolute`: Hacer URL absoluta +- `ensureProtocol`: Asegurar que URL tenga protocolo +- `cleanQuery`: Limpiar cadena de consulta +- `removeHash`: Eliminar hash de URL +- `urlEncode`: Codificar URL +- `urlDecode`: Decodificar URL +- `sanitizeUrl`: Sanitizar URL + +--- + +### 4. Validación (`validation.ts`) + +Patrones para seguridad y validación. + +#### SECURITY_PATTERNS + +Comprobaciones de seguridad: + +- `externalScript`: Detectar etiquetas de script externas +- `externalCss`: Detectar enlaces CSS externos +- `externalImage`: Detectar imágenes externas +- `externalUrl`: Coincidir URLs externas + +#### DANGEROUS_FUNCTION_PATTERNS + +Funciones de JavaScript peligrosas: + +- `eval`: Detectar eval() +- `functionConstructor`: Detectar Function() +- `setTimeout`: Detectar setTimeout() +- `setInterval`: Detectar setInterval() +- `documentWrite`: Detectar document.write +- `documentWriteln`: Detectar document.writeln +- `innerHTML`: Detectar asignaciones innerHTML +- `outerHTML`: Detectar asignaciones outerHTML + +#### SENSITIVE_DATA_PATTERNS + +Detección de datos sensibles: + +- `apiKey`: Detectar referencias a API key +- `secret`: Detectar referencias a secretos +- `password`: Detectar referencias a contraseñas +- `token`: Detectar referencias a tokens +- `privateKey`: Detectar referencias a clave privada +- `accessKey`: Detectar referencias a clave de acceso + +--- + +### 5. Utilidad (`utility.ts`) + +Patrones de utilidad general. + +#### PATH_PATTERNS + +Manipulación de rutas: + +- `backslashes`: Coincidir barras invertidas +- `leadingSlash`: Eliminar barras iniciales de rutas + +#### SANITIZATION_PATTERNS + +Sanitización de texto: + +- `themeName`: Sanitizar nombres de temas a alfanuméricos + +#### JSON_PARSING_PATTERNS + +Análisis JSON: + +- `liquidSchema`: Extraer bloques de esquema Liquid +- `trailingComma`: Eliminar comas finales +- `multipleCommas`: Corregir comas múltiples +- `lineComment`: Coincidir comentarios de línea +- `blockComment`: Coincidir comentarios de bloque + +--- + +## Uso + +Importar patrones desde el índice principal: + +```typescript +import { + LIQUID_OBJECT_PATTERNS, + MINIFICATION_PATTERNS, + ESCAPE_PATTERNS, + // ... otros patrones +} from '../../../lib/regex-patterns'; + +// Usar en código +const match = content.match(LIQUID_OBJECT_PATTERNS.products); +``` + +## Modificadores de Patrones + +Los patrones utilizan estas banderas comunes: + +- `g`: Coincidencia global (buscar todas las coincidencias) +- `i`: Insensible a mayúsculas +- `m`: Modo multilínea +- `u`: Modo unicode + +## Beneficios de la Centralización + +1. **Mantenibilidad**: Actualizar patrones en un solo lugar +2. **Consistencia**: Mismo patrón en todos los usos +3. **Testabilidad**: Probar patrones de forma independiente +4. **Documentación**: Propósito claro para cada patrón +5. **Reutilización**: Compartir patrones entre módulos + +## Actualización de Patrones + +Al actualizar patrones: + +1. Documentar el cambio en este README +2. Actualizar comentarios JSDoc en el archivo de patrones +3. Ejecutar linter para verificar que no hay errores +4. Probar módulos afectados + +## Contribuir + +Al añadir nuevos patrones: + +1. Colocar en el archivo de categoría apropiado +2. Exportar desde ese archivo +3. Exportar desde `index.ts` +4. Documentar en este README +5. Usar nombres descriptivos diff --git a/packages/liquid-forge/lib/regex-patterns/filters.ts b/packages/liquid-forge/lib/regex-patterns/filters.ts new file mode 100644 index 00000000..815283b5 --- /dev/null +++ b/packages/liquid-forge/lib/regex-patterns/filters.ts @@ -0,0 +1,40 @@ +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const ESCAPE_PATTERNS = { + ampersand: /&/g, + lessThan: //g, + doubleQuote: /"/g, + apostrophe: /'/g, +} as const; + +export const HANDLE_PATTERNS = { + aVariants: /[áàäâã]/g, + eVariants: /[éèëê]/g, + iVariants: /[íìïî]/g, + oVariants: /[óòöôõ]/g, + uVariants: /[úùüû]/g, + enye: /[ñ]/g, + cCedilla: /[ç]/g, + nonAlphanumeric: /[^a-z0-9]/g, + multipleDashes: /-+/g, + leadingTrailingDash: /^-|-$/g, +} as const; + +export const URL_PATTERNS = { + trailingSlashes: /\/+$/, +} as const; diff --git a/packages/liquid-forge/lib/regex-patterns/index.ts b/packages/liquid-forge/lib/regex-patterns/index.ts new file mode 100644 index 00000000..fcffa7f0 --- /dev/null +++ b/packages/liquid-forge/lib/regex-patterns/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './liquid-syntax'; +export * from './minification'; +export * from './filters'; +export * from './validation'; +export * from './utility'; diff --git a/packages/liquid-forge/lib/regex-patterns/liquid-syntax.ts b/packages/liquid-forge/lib/regex-patterns/liquid-syntax.ts new file mode 100644 index 00000000..1ce6bec9 --- /dev/null +++ b/packages/liquid-forge/lib/regex-patterns/liquid-syntax.ts @@ -0,0 +1,79 @@ +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Patrones para detección de sintaxis Liquid + */ +export const LIQUID_OBJECT_PATTERNS = { + products: /\{\{\s*products\s*[\|\}]/g, + collections: /\{\{\s*collections\s*[\|\}]/g, + product: /\{\{\s*product\./g, + collection: /\{\{\s*collection\./g, + linklists: /\{\{\s*linklists\./g, + shop: /\{\{\s*shop\./g, + pages: /\{\{\s*pages\s*[\|\}]/g, + page: /\{\{\s*page\./g, + blog: /\{\{\s*blog\./g, + checkout: /\{\{\s*checkout\./g, + relatedProducts: /related_products|product\s*\|\s*related/g, + pagination: /\{\%\s*paginate/g, +} as const; + +export const LIQUID_TAG_PATTERNS = { + paginate: /\{\%\s*paginate\s+([^%]+)\%\}/g, + section: /\{\%\s*section\s+['"]([^'"]+)['"]\s*\%\}/g, + render: /\{\%\s*render\s+['"]([^'"]+)['"]/g, + include: /\{\%\s*include\s+['"]([^'"]+)['"]/g, +} as const; + +export const LIQUID_COLLECTION_ACCESS_PATTERNS = { + bracketNotation: /collections\[['"]([^'"]+)['"]\]/g, + dotNotation: /collections\.([a-zA-Z0-9_-]+)(?!\.\w)/g, + specificAccess: /collections\[['"]([^'"]+)['"]\]|collections\.([a-zA-Z0-9_-]+)(?!\.\w)/g, + collectionProducts: /collections\.([a-zA-Z0-9_-]+)\.products/g, + specificPage: /pages\[['"]([^'"]+)['"]\]|pages\.([a-zA-Z0-9_-]+)(?!\.\w)/g, + specificProduct: /products\[['"]([^'"]+)['"]\]/g, + extractCollectionHandle: /collections\.([a-zA-Z0-9_-]+)/, + extractPagesBracket: /pages\[['"]([^'"]+)['"]\]/g, + extractPagesDot: /pages\.([a-zA-Z0-9_-]+)(?!\.\w)/g, + extractPagesHandle: /pages\.([a-zA-Z0-9_-]+)/, + pagesBracketExtract: /pages\[['"]([^'"]+)['"]\]/, +} as const; + +export const LIQUID_FILTER_PATTERNS = { + productRelated: /related_products[^}]*limit:\s*(\d+)/i, + productsLimit: /products[^}]*limit:\s*(\d+)/i, + collectionsLimit: /collections[^}]*limit:\s*(\d+)/i, + generalLimit: /limit:\s*(\d+)/i, + paginateBy: /\s+by\s+/, +} as const; + +export const LIQUID_OPTION_EXTRACTOR_PATTERNS = { + sectionName: /section\s+['"]([^'"]+)['"]/i, + snippetName: /(?:render|include)\s+['"]([^'"]+)['"]/i, + collectionHandle: /collections\[['"]([^'"]+)['"]\]/, +} as const; + +export const LIQUID_PAGINATION_PATTERNS = { + paginate: /\{%\s*paginate\s+(.+?)\s*%\}/, + policies: /for\s+item\s+in\s+policies|for\s+policy\s+in\s+policies|\{\{\s*policies\s*[\|\}]/g, + pagesLimit: /pages[^}]*limit:\s*(\d+)/i, +} as const; + +export const LIQUID_VARIABLE_PATTERNS = { + variable: /\{\{\s*([^}]+)\s*\}\}/g, + cleanVariableTags: /\{\{\s*|\s*\}\}/g, +} as const; diff --git a/packages/liquid-forge/lib/regex-patterns/minification.ts b/packages/liquid-forge/lib/regex-patterns/minification.ts new file mode 100644 index 00000000..e4769c55 --- /dev/null +++ b/packages/liquid-forge/lib/regex-patterns/minification.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const MINIFICATION_PATTERNS = { + htmlComment: //g, + blockComment: /\/\*[\s\S]*?\*\//g, + lineComment: /\/\/.*$/gm, + trimLines: /^[ \t]+|[ \t]+$/gm, + multipleNewlines: /\n\s*\n\s*\n/g, + multipleSpaces: /\s+/g, + operatorSpaces: /\s*([=+\-*/%&|^!~?:,;{}()\[\]<>])\s*/g, + liquidComment: /\{%\s*comment\s*%\}[\s\S]*?\{%\s*endcomment\s*%\}/g, + styleBlock: /]*>([\s\S]*?)<\/style\b[^>]*>/gi, + scriptBlock: /]*>([\s\S]*?)<\/script\b[^>]*>/gi, +} as const; + +export const CSS_MINIFICATION_PATTERNS = { + comments: /\/\*[\s\S]*?\*\//g, + spaces: /\s+/g, + syntax: /\s*([{}|:;,])\s*/g, +} as const; + +export const CSS_OPTIMIZATION_PATTERNS = { + comments: /\/\*[\s\S]*?\*\//g, + braces: /\s*{\s*|\s*}\s*/g, + semicolons: /\s*;\s*/g, + emptyLines: /^\s*[\r\n]/gm, +} as const; diff --git a/packages/liquid-forge/lib/regex-patterns/utility.ts b/packages/liquid-forge/lib/regex-patterns/utility.ts new file mode 100644 index 00000000..28e840cc --- /dev/null +++ b/packages/liquid-forge/lib/regex-patterns/utility.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const PATH_PATTERNS = { + backslashes: /\\/g, + leadingSlash: /^\/+/g, +} as const; + +export const SANITIZATION_PATTERNS = { + themeName: /[^a-z0-9]/g, +} as const; + +export const JSON_PARSING_PATTERNS = { + liquidSchema: /{%\s*schema\s*%}([\s\S]*?){%\s*endschema\s*%}/i, + trailingComma: /,(\s*[}\]])/g, + multipleCommas: /,,+/g, + lineComment: /\/\/.*$/gm, + blockComment: /\/\*[\s\S]*?\*\//g, +} as const; diff --git a/packages/liquid-forge/lib/regex-patterns/validation.ts b/packages/liquid-forge/lib/regex-patterns/validation.ts new file mode 100644 index 00000000..7ad46d9d --- /dev/null +++ b/packages/liquid-forge/lib/regex-patterns/validation.ts @@ -0,0 +1,51 @@ +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const SECURITY_PATTERNS = { + externalScript: /]*src=["'](https?:\/\/[^"']+)["'][^>]*>/gi, + externalCss: /]*href=["'](https?:\/\/[^"']+)["'][^>]*>/gi, + externalImage: /src=["'](https?:\/\/[^"']+)["']/gi, + externalUrl: /https?:\/\/[^\s"']+/gi, +} as const; + +export const DANGEROUS_FUNCTION_PATTERNS = { + eval: /eval\s*\(/, + functionConstructor: /Function\s*\(/, + setTimeout: /setTimeout\s*\(/, + setInterval: /setInterval\s*\(/, + documentWrite: /document\.write/, + documentWriteln: /document\.writeln/, + innerHTML: /innerHTML\s*=/, + outerHTML: /outerHTML\s*=/, +} as const; + +export const SENSITIVE_DATA_PATTERNS = { + apiKey: /api_key/, + secret: /secret/, + password: /password/, + token: /token/, + privateKey: /private_key/, + accessKey: /access_key/, +} as const; + +export const IMAGE_PATTERNS = { + imgTag: /]*src=["']([^"']+)["'][^>]*>/gi, + imgSrc: /src=["']([^"']+)["']/, +} as const; + +export const SPACE_PATTERNS = { + multipleSpaces: /\s{2,}/g, +} as const; diff --git a/packages/liquid-forge/liquid/filters/base-filters.ts b/packages/liquid-forge/liquid/filters/base-filters.ts index 28b70d20..02e9f094 100644 --- a/packages/liquid-forge/liquid/filters/base-filters.ts +++ b/packages/liquid-forge/liquid/filters/base-filters.ts @@ -15,6 +15,7 @@ */ import type { LiquidFilter } from '../../types'; +import { ESCAPE_PATTERNS, HANDLE_PATTERNS, URL_PATTERNS } from '../../lib/regex-patterns'; /** * Filtro para formatear fechas @@ -67,16 +68,16 @@ export const handleizeFilter: LiquidFilter = { return text .toLowerCase() .trim() - .replace(/[áàäâã]/g, 'a') - .replace(/[éèëê]/g, 'e') - .replace(/[íìïî]/g, 'i') - .replace(/[óòöôõ]/g, 'o') - .replace(/[úùüû]/g, 'u') - .replace(/[ñ]/g, 'n') - .replace(/[ç]/g, 'c') - .replace(/[^a-z0-9]/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); + .replace(HANDLE_PATTERNS.aVariants, 'a') + .replace(HANDLE_PATTERNS.eVariants, 'e') + .replace(HANDLE_PATTERNS.iVariants, 'i') + .replace(HANDLE_PATTERNS.oVariants, 'o') + .replace(HANDLE_PATTERNS.uVariants, 'u') + .replace(HANDLE_PATTERNS.enye, 'n') + .replace(HANDLE_PATTERNS.cCedilla, 'c') + .replace(HANDLE_PATTERNS.nonAlphanumeric, '-') + .replace(HANDLE_PATTERNS.multipleDashes, '-') + .replace(HANDLE_PATTERNS.leadingTrailingDash, ''); }, }; @@ -119,11 +120,11 @@ export const escapeFilter: LiquidFilter = { } return text - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); + .replace(ESCAPE_PATTERNS.ampersand, '&') + .replace(ESCAPE_PATTERNS.lessThan, '<') + .replace(ESCAPE_PATTERNS.greaterThan, '>') + .replace(ESCAPE_PATTERNS.doubleQuote, '"') + .replace(ESCAPE_PATTERNS.apostrophe, '''); }, }; diff --git a/packages/liquid-forge/liquid/filters/ecommerce-filters.ts b/packages/liquid-forge/liquid/filters/ecommerce-filters.ts index c820bcb3..3c5ab4fe 100644 --- a/packages/liquid-forge/liquid/filters/ecommerce-filters.ts +++ b/packages/liquid-forge/liquid/filters/ecommerce-filters.ts @@ -15,6 +15,7 @@ */ import type { LiquidFilter } from '../../types'; +import { PATH_PATTERNS } from '../../lib/regex-patterns'; /** * Interfaz para parámetros de optimización de imágenes @@ -135,7 +136,7 @@ export const imgUrlFilter: LiquidFilter = { // Si es una ruta relativa, construir URL completa const baseImageUrl = '/images'; - const cleanImageUrl = url.replace(/^\/+/, ''); + const cleanImageUrl = url.replace(PATH_PATTERNS.leadingSlash, ''); const fullUrl = `${baseImageUrl}/${cleanImageUrl}`; return buildOptimizedImageUrl(fullUrl, params); diff --git a/packages/liquid-forge/liquid/filters/html-filters.ts b/packages/liquid-forge/liquid/filters/html-filters.ts index 4533a2b7..c28df14d 100644 --- a/packages/liquid-forge/liquid/filters/html-filters.ts +++ b/packages/liquid-forge/liquid/filters/html-filters.ts @@ -16,6 +16,7 @@ import { logger } from '../../lib/logger'; import type { LiquidFilter } from '../../types'; +import { PATH_PATTERNS } from '../../lib/regex-patterns'; /** * Filtro asset_url - Para archivos estáticos (CSS, JS, imágenes de tema) @@ -28,7 +29,7 @@ export const assetUrlFilter: LiquidFilter = { return ''; } - const cleanFilename = filename.replace(/^\/+/, ''); + const cleanFilename = filename.replace(PATH_PATTERNS.leadingSlash, ''); // Acceder al storeId desde el contexto de LiquidJS const storeId = this.context?.getSync(['shop', 'storeId']); @@ -199,7 +200,7 @@ export const inlineAssetContentFilter: LiquidFilter = { return ''; } - const cleanFilename = filename.replace(/^\/+/, ''); + const cleanFilename = filename.replace(PATH_PATTERNS.leadingSlash, ''); // Acceder al storeId desde el contexto de LiquidJS const storeId = this.context?.getSync(['shop', 'storeId']); diff --git a/packages/liquid-forge/liquid/tags/core/render-tag.ts b/packages/liquid-forge/liquid/tags/core/render-tag.ts index a48fb67a..49516b37 100644 --- a/packages/liquid-forge/liquid/tags/core/render-tag.ts +++ b/packages/liquid-forge/liquid/tags/core/render-tag.ts @@ -121,7 +121,8 @@ export class RenderTag extends Tag { } // Usar el TemplateLoader para cargar el snippet - const { templateLoader } = await import('../../../services/templates/template-loader'); + const { TemplateLoader } = await import('../../../services/templates/template-loader'); + const templateLoader = TemplateLoader.getInstance(); // Los snippets están en la carpeta 'snippets' const snippetFileName = snippetName.endsWith('.liquid') ? snippetName : `${snippetName}.liquid`; diff --git a/packages/liquid-forge/liquid/tags/data/javascript-tag.ts b/packages/liquid-forge/liquid/tags/data/javascript-tag.ts index 08836d17..da21d946 100644 --- a/packages/liquid-forge/liquid/tags/data/javascript-tag.ts +++ b/packages/liquid-forge/liquid/tags/data/javascript-tag.ts @@ -17,23 +17,18 @@ import { Tag, TagToken, Context, TopLevelToken, Liquid, TokenKind } from 'liquidjs'; import { AssetCollector } from '../../../services/rendering/asset-collector'; import { logger } from '../../../lib/logger'; +import { MINIFICATION_PATTERNS } from '../../../lib/regex-patterns'; /** * Optimiza y limpia el JavaScript generado */ function optimizeJS(js: string): string { - return ( - js - // Remover comentarios de línea (// ...) - .replace(/\/\/.*$/gm, '') - // Remover comentarios de bloque (/* ... */) - .replace(/\/\*[\s\S]*?\*\//g, '') - // Remover espacios múltiples - .replace(/\s+/g, ' ') - // Limpiar líneas vacías - .replace(/^\s*[\r\n]/gm, '') - .trim() - ); + return js + .replace(MINIFICATION_PATTERNS.lineComment, '') + .replace(MINIFICATION_PATTERNS.blockComment, '') + .replace(MINIFICATION_PATTERNS.multipleSpaces, ' ') + .replace(/^\s*[\r\n]/gm, '') + .trim(); } /** diff --git a/packages/liquid-forge/liquid/tags/styling/style-tag.ts b/packages/liquid-forge/liquid/tags/styling/style-tag.ts index 6636cc47..ac68174b 100644 --- a/packages/liquid-forge/liquid/tags/styling/style-tag.ts +++ b/packages/liquid-forge/liquid/tags/styling/style-tag.ts @@ -17,25 +17,20 @@ import { Tag, TagToken, Context, TopLevelToken, Liquid, TokenKind } from 'liquidjs'; import { AssetCollector } from '../../../services/rendering/asset-collector'; import { logger } from '../../../lib/logger'; +import { CSS_OPTIMIZATION_PATTERNS, MINIFICATION_PATTERNS } from '../../../lib/regex-patterns'; /** * Optimiza y limpia el CSS generado */ function optimizeCSS(css: string): string { - return ( - css - // Remover comentarios CSS - .replace(/\/\*[\s\S]*?\*\//g, '') - // Remover espacios múltiples (pero preservar selectores) - .replace(/\s+/g, ' ') - // Limpiar espacios alrededor de llaves y punto y coma (pero NO en selectores) - .replace(/\s*{\s*/g, '{') - .replace(/\s*}\s*/g, '}') - .replace(/\s*;\s*/g, ';') - // Limpiar líneas vacías - .replace(/^\s*[\r\n]/gm, '') - .trim() - ); + return css + .replace(CSS_OPTIMIZATION_PATTERNS.comments, '') + .replace(MINIFICATION_PATTERNS.multipleSpaces, ' ') + .replace(/\s*{\s*/g, '{') + .replace(/\s*}\s*/g, '}') + .replace(/\s*;\s*/g, ';') + .replace(CSS_OPTIMIZATION_PATTERNS.emptyLines, '') + .trim(); } /** diff --git a/packages/liquid-forge/services/core/data-transformer.ts b/packages/liquid-forge/services/core/data-transformer.ts index 359b1a10..074f15ff 100644 --- a/packages/liquid-forge/services/core/data-transformer.ts +++ b/packages/liquid-forge/services/core/data-transformer.ts @@ -14,24 +14,23 @@ * limitations under the License. */ +import { HANDLE_PATTERNS } from '../../lib/regex-patterns'; + export class DataTransformer { - /** - * Crea un handle SEO-friendly a partir de un texto - */ public static createHandle(text: string): string { return text .toLowerCase() .trim() - .replace(/[áàäâã]/g, 'a') - .replace(/[éèëê]/g, 'e') - .replace(/[íìïî]/g, 'i') - .replace(/[óòöôõ]/g, 'o') - .replace(/[úùüû]/g, 'u') - .replace(/[ñ]/g, 'n') - .replace(/[ç]/g, 'c') - .replace(/[^a-z0-9]/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); + .replace(HANDLE_PATTERNS.aVariants, 'a') + .replace(HANDLE_PATTERNS.eVariants, 'e') + .replace(HANDLE_PATTERNS.iVariants, 'i') + .replace(HANDLE_PATTERNS.oVariants, 'o') + .replace(HANDLE_PATTERNS.uVariants, 'u') + .replace(HANDLE_PATTERNS.enye, 'n') + .replace(HANDLE_PATTERNS.cCedilla, 'c') + .replace(HANDLE_PATTERNS.nonAlphanumeric, '-') + .replace(HANDLE_PATTERNS.multipleDashes, '-') + .replace(HANDLE_PATTERNS.leadingTrailingDash, ''); } /** diff --git a/packages/liquid-forge/services/templates/parsing/liquid-syntax-detector.ts b/packages/liquid-forge/services/templates/parsing/liquid-syntax-detector.ts index e1420342..529afaf7 100644 --- a/packages/liquid-forge/services/templates/parsing/liquid-syntax-detector.ts +++ b/packages/liquid-forge/services/templates/parsing/liquid-syntax-detector.ts @@ -15,6 +15,14 @@ */ import type { DataLoadOptions, DataRequirement, TemplateAnalysis } from '../analysis/template-analyzer'; +import { + LIQUID_OBJECT_PATTERNS, + LIQUID_COLLECTION_ACCESS_PATTERNS, + LIQUID_FILTER_PATTERNS, + LIQUID_TAG_PATTERNS, + LIQUID_PAGINATION_PATTERNS, + LIQUID_OPTION_EXTRACTOR_PATTERNS, +} from '../../../lib/regex-patterns'; /** * Tipo para detectores de objetos Liquid @@ -34,14 +42,16 @@ type PaginationHandler = (match: string, analysis: TemplateAnalysis) => void; */ const loadOptionsExtractors: Record DataLoadOptions> = { products: (content: string) => { - const limitMatch = content.match(/products[^}]*limit:\s*(\d+)/i); + const limitMatch = content.match(LIQUID_FILTER_PATTERNS.productsLimit); return { limit: limitMatch ? parseInt(limitMatch[1], 10) : undefined, }; }, collection_products: (content: string) => { - const collectionMatch = content.match(/collections\.([a-zA-Z0-9_-]+)\.products[^}]*limit:\s*(\d+)/i); + const collectionMatch = content.match( + new RegExp(`collections\\.([a-zA-Z0-9_-]+)\\.products[^}]*limit:\\s*(\\d+)`, 'i') + ); if (collectionMatch) { return { collectionHandle: collectionMatch[1], @@ -49,12 +59,12 @@ const loadOptionsExtractors: Record DataLo }; } - const collectionOnlyMatch = content.match(/collections\.([a-zA-Z0-9_-]+)\.products/i); + const collectionOnlyMatch = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.collectionProducts); return collectionOnlyMatch ? { collectionHandle: collectionOnlyMatch[1], limit: 8 } : { limit: 8 }; }, collections: (content: string) => { - const limitMatch = content.match(/collections[^}]*limit:\s*(\d+)/i); + const limitMatch = content.match(LIQUID_FILTER_PATTERNS.collectionsLimit); return { limit: limitMatch ? parseInt(limitMatch[1], 10) : undefined, }; @@ -64,20 +74,18 @@ const loadOptionsExtractors: Record DataLo specific_collection: (content: string) => { const handles: string[] = []; - // Detectar collections['handle'] y collections["handle"] - const bracketMatches = content.match(/collections\[['"]([^'"]+)['"]\]/g); + const bracketMatches = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.bracketNotation); if (bracketMatches) { bracketMatches.forEach((match) => { - const handleMatch = match.match(/collections\[['"]([^'"]+)['"]\]/); + const handleMatch = match.match(LIQUID_COLLECTION_ACCESS_PATTERNS.bracketNotation); if (handleMatch) handles.push(handleMatch[1]); }); } - // Detectar collections.handle (acceso directo por propiedad) - const dotMatches = content.match(/collections\.([a-zA-Z0-9_-]+)(?!\.\w)/g); + const dotMatches = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.dotNotation); if (dotMatches) { dotMatches.forEach((match) => { - const handleMatch = match.match(/collections\.([a-zA-Z0-9_-]+)/); + const handleMatch = match.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractCollectionHandle); if (handleMatch && !handles.includes(handleMatch[1])) { handles.push(handleMatch[1]); } @@ -90,11 +98,10 @@ const loadOptionsExtractors: Record DataLo specific_product: (content: string) => { const handles: string[] = []; - // Detectar products['handle'] y products["handle"] - const bracketMatches = content.match(/products\[['"]([^'"]+)['"]\]/g); + const bracketMatches = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.specificProduct); if (bracketMatches) { bracketMatches.forEach((match) => { - const handleMatch = match.match(/products\[['"]([^'"]+)['"]\]/); + const handleMatch = match.match(LIQUID_COLLECTION_ACCESS_PATTERNS.specificProduct); if (handleMatch) handles.push(handleMatch[1]); }); } @@ -104,14 +111,13 @@ const loadOptionsExtractors: Record DataLo products_by_collection: (content: string) => { const handles: string[] = []; - const limitMatch = content.match(/limit:\s*(\d+)/i); + const limitMatch = content.match(LIQUID_FILTER_PATTERNS.generalLimit); const limit = limitMatch ? parseInt(limitMatch[1], 10) : 8; - // Extraer handles de colecciones mencionadas para cargar sus productos - const collectionRefs = content.match(/collections\.([a-zA-Z0-9_-]+)\.products/g); + const collectionRefs = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.collectionProducts); if (collectionRefs) { collectionRefs.forEach((ref) => { - const handleMatch = ref.match(/collections\.([a-zA-Z0-9_-]+)/); + const handleMatch = ref.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractCollectionHandle); if (handleMatch && !handles.includes(handleMatch[1])) { handles.push(handleMatch[1]); } @@ -122,7 +128,7 @@ const loadOptionsExtractors: Record DataLo }, related_products: (content: string) => { - const limitMatch = content.match(/related_products[^}]*limit:\s*(\d+)/i); + const limitMatch = content.match(LIQUID_FILTER_PATTERNS.productRelated); return { limit: limitMatch ? parseInt(limitMatch[1], 10) : 4, }; @@ -131,20 +137,18 @@ const loadOptionsExtractors: Record DataLo specific_page: (content: string) => { const handles: string[] = []; - // Detectar pages['handle'] y pages["handle"] - const bracketMatches = content.match(/pages\[['"]([^'"]+)['"]\]/g); + const bracketMatches = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractPagesBracket); if (bracketMatches) { bracketMatches.forEach((match) => { - const handleMatch = match.match(/pages\[['"]([^'"]+)['"]\]/); + const handleMatch = match.match(LIQUID_COLLECTION_ACCESS_PATTERNS.pagesBracketExtract); if (handleMatch) handles.push(handleMatch[1]); }); } - // Detectar pages.handle (acceso directo por propiedad) - const dotMatches = content.match(/pages\.([a-zA-Z0-9_-]+)(?!\.\w)/g); + const dotMatches = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractPagesDot); if (dotMatches) { dotMatches.forEach((match) => { - const handleMatch = match.match(/pages\.([a-zA-Z0-9_-]+)/); + const handleMatch = match.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractPagesHandle); if (handleMatch && !handles.includes(handleMatch[1])) { handles.push(handleMatch[1]); } @@ -155,7 +159,7 @@ const loadOptionsExtractors: Record DataLo }, pages: (content: string) => { - const limitMatch = content.match(/pages[^}]*limit:\s*(\d+)/i); + const limitMatch = content.match(LIQUID_PAGINATION_PATTERNS.pagesLimit); return { limit: limitMatch ? parseInt(limitMatch[1], 10) : 10, }; @@ -174,20 +178,18 @@ const loadOptionsExtractors: Record DataLo page: (content: string) => { const handles: string[] = []; - // Detectar pages['handle'] y pages["handle"] - const bracketMatches = content.match(/pages\[['"]([^'"]+)['"]\]/g); + const bracketMatches = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractPagesBracket); if (bracketMatches) { bracketMatches.forEach((match) => { - const handleMatch = match.match(/pages\[['"]([^'"]+)['"]\]/); + const handleMatch = match.match(LIQUID_COLLECTION_ACCESS_PATTERNS.pagesBracketExtract); if (handleMatch) handles.push(handleMatch[1]); }); } - // Detectar pages.handle (acceso directo por propiedad) - const dotMatches = content.match(/pages\.([a-zA-Z0-9_-]+)(?!\.\w)/g); + const dotMatches = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractPagesDot); if (dotMatches) { dotMatches.forEach((match) => { - const handleMatch = match.match(/pages\.([a-zA-Z0-9_-]+)/); + const handleMatch = match.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractPagesHandle); if (handleMatch && !handles.includes(handleMatch[1])) { handles.push(handleMatch[1]); } @@ -207,92 +209,85 @@ const loadOptionsExtractors: Record DataLo */ const objectDetectors: Record = { products: { - pattern: /\{\{\s*products\s*[\|\}]/g, + pattern: LIQUID_OBJECT_PATTERNS.products, optionsExtractor: loadOptionsExtractors.products, }, collection_products: { - pattern: /collections\.([a-zA-Z0-9_-]+)\.products/g, + pattern: LIQUID_COLLECTION_ACCESS_PATTERNS.collectionProducts, optionsExtractor: loadOptionsExtractors.collection_products, }, collections: { - pattern: /\{\{\s*collections\s*[\|\}]/g, + pattern: LIQUID_OBJECT_PATTERNS.collections, optionsExtractor: loadOptionsExtractors.collections, }, - - // Nuevos detectores para acceso específico specific_collection: { - pattern: /collections\[['"]([^'"]+)['"]\]|collections\.([a-zA-Z0-9_-]+)(?!\.\w)/g, + pattern: LIQUID_COLLECTION_ACCESS_PATTERNS.specificAccess, optionsExtractor: loadOptionsExtractors.specific_collection, }, specific_product: { - pattern: /products\[['"]([^'"]+)['"]\]/g, + pattern: LIQUID_COLLECTION_ACCESS_PATTERNS.specificProduct, optionsExtractor: loadOptionsExtractors.specific_product, }, products_by_collection: { - pattern: /collections\.([a-zA-Z0-9_-]+)\.products/g, + pattern: LIQUID_COLLECTION_ACCESS_PATTERNS.collectionProducts, optionsExtractor: loadOptionsExtractors.products_by_collection, }, related_products: { - pattern: /related_products|product\s*\|\s*related/g, + pattern: LIQUID_OBJECT_PATTERNS.relatedProducts, optionsExtractor: loadOptionsExtractors.related_products, }, product: { - pattern: /\{\{\s*product\./g, + pattern: LIQUID_OBJECT_PATTERNS.product, optionsExtractor: loadOptionsExtractors.product, }, collection: { - pattern: /\{\{\s*collection\./g, + pattern: LIQUID_OBJECT_PATTERNS.collection, optionsExtractor: loadOptionsExtractors.collection, }, linklists: { - pattern: /\{\{\s*linklists\./g, + pattern: LIQUID_OBJECT_PATTERNS.linklists, optionsExtractor: loadOptionsExtractors.linklists, }, shop: { - pattern: /\{\{\s*shop\./g, + pattern: LIQUID_OBJECT_PATTERNS.shop, optionsExtractor: loadOptionsExtractors.shop, }, specific_page: { - pattern: /pages\[['"]([^'"]+)['"]\]|pages\.([a-zA-Z0-9_-]+)(?!\.\w)/g, + pattern: LIQUID_COLLECTION_ACCESS_PATTERNS.specificPage, optionsExtractor: loadOptionsExtractors.specific_page, }, pages: { - pattern: /\{\{\s*pages\s*[\|\}]/g, + pattern: LIQUID_OBJECT_PATTERNS.pages, optionsExtractor: loadOptionsExtractors.pages, }, policies: { - pattern: /for\s+item\s+in\s+policies|for\s+policy\s+in\s+policies|\{\{\s*policies\s*[\|\}]/g, + pattern: LIQUID_PAGINATION_PATTERNS.policies, optionsExtractor: loadOptionsExtractors.policies, }, page: { - pattern: /\{\{\s*page\./g, + pattern: LIQUID_OBJECT_PATTERNS.page, optionsExtractor: loadOptionsExtractors.page, }, blog: { - pattern: /\{\{\s*blog\./g, + pattern: LIQUID_OBJECT_PATTERNS.blog, optionsExtractor: loadOptionsExtractors.blog, }, pagination: { - pattern: /\{\%\s*paginate/g, + pattern: LIQUID_OBJECT_PATTERNS.pagination, optionsExtractor: loadOptionsExtractors.pagination, }, checkout: { - pattern: /\{\{\s*checkout\./g, + pattern: LIQUID_OBJECT_PATTERNS.checkout, optionsExtractor: loadOptionsExtractors.checkout, }, checkout_confirmation: { - pattern: /\{\{\s*checkout\./g, + pattern: LIQUID_OBJECT_PATTERNS.checkout, optionsExtractor: loadOptionsExtractors.checkout_confirmation, }, }; export class LiquidSyntaxDetector { - private static readonly TAG_PATTERNS = { - paginate: /\{\%\s*paginate\s+([^%]+)\%\}/g, - section: /\{\%\s*section\s+['"]([^'"]+)['"]\s*\%\}/g, - render: /\{\%\s*render\s+['"]([^'"]+)['"]/g, - include: /\{\%\s*include\s+['"]([^'"]+)['"]/g, - }; + private static readonly TAG_PATTERNS = LIQUID_TAG_PATTERNS; public static detectLiquidObjects(content: string, analysis: TemplateAnalysis): void { for (const [objectType, detector] of Object.entries(objectDetectors)) { @@ -306,14 +301,12 @@ export class LiquidSyntaxDetector { } public static detectPagination(content: string, analysis: TemplateAnalysis): void { - const paginatePattern = /\{%\s*paginate\s+(.+?)\s*%\}/; - const match = content.match(paginatePattern); + const match = content.match(LIQUID_PAGINATION_PATTERNS.paginate); if (match) { analysis.hasPagination = true; - const paginatedObject = match[1].split(/\s+by\s+/)[0]; + const paginatedObject = match[1].split(LIQUID_FILTER_PATTERNS.paginateBy)[0]; - // Determinar si se paginan productos o colecciones if (paginatedObject.includes('products')) { analysis.requiredData.set('products', {}); } else if (paginatedObject.includes('collections')) { @@ -324,20 +317,18 @@ export class LiquidSyntaxDetector { } public static detectDependencies(content: string, analysis: TemplateAnalysis): void { - // Detectar secciones this.extractMatches(content, this.TAG_PATTERNS.section, (match) => { - const sectionName = this.extractName(match, /section\s+['"]([^'"]+)['"]/i); + const sectionName = this.extractName(match, LIQUID_OPTION_EXTRACTOR_PATTERNS.sectionName); if (sectionName) { analysis.usedSections.push(sectionName); analysis.dependencies.push(`sections/${sectionName}.liquid`); } }); - // Detectar snippets (render e include) const snippetPatterns = [this.TAG_PATTERNS.render, this.TAG_PATTERNS.include]; for (const pattern of snippetPatterns) { this.extractMatches(content, pattern, (match) => { - const snippetName = this.extractName(match, /(?:render|include)\s+['"]([^'"]+)['"]/i); + const snippetName = this.extractName(match, LIQUID_OPTION_EXTRACTOR_PATTERNS.snippetName); if (snippetName) { analysis.dependencies.push(`snippets/${snippetName}.liquid`); } diff --git a/packages/liquid-forge/services/templates/parsing/schema-parser.ts b/packages/liquid-forge/services/templates/parsing/schema-parser.ts index 53ae79fd..0e437d3d 100644 --- a/packages/liquid-forge/services/templates/parsing/schema-parser.ts +++ b/packages/liquid-forge/services/templates/parsing/schema-parser.ts @@ -14,8 +14,10 @@ * limitations under the License. */ +import { JSON_PARSING_PATTERNS, MINIFICATION_PATTERNS } from '../../../lib/regex-patterns'; + export class SchemaParser { - private static readonly SCHEMA_REGEX = /{%\s*schema\s*%}([\s\S]*?){%\s*endschema\s*%}/i; + private static readonly SCHEMA_REGEX = JSON_PARSING_PATTERNS.liquidSchema; /** * Extrae y parsea el schema completo de un template (una sola vez) @@ -44,11 +46,11 @@ export class SchemaParser { */ private cleanJSON(content: string): string { return content - .replace(/\/\/.*$/gm, '') // Comentarios // - .replace(/\/\*[\s\S]*?\*\//g, '') // Comentarios /* */ - .replace(/,(\s*[}\]])/g, '$1') // Comas finales - .replace(/,,+/g, ',') // Comas dobles - .replace(/\s+/g, ' ') // Espacios extra + .replace(JSON_PARSING_PATTERNS.lineComment, '') + .replace(JSON_PARSING_PATTERNS.blockComment, '') + .replace(JSON_PARSING_PATTERNS.trailingComma, '$1') + .replace(JSON_PARSING_PATTERNS.multipleCommas, ',') + .replace(MINIFICATION_PATTERNS.multipleSpaces, ' ') .trim(); } diff --git a/packages/liquid-forge/services/templates/sync/template-dev-synchronizer.ts b/packages/liquid-forge/services/templates/sync/template-dev-synchronizer.ts index 77e6a90e..25769277 100644 --- a/packages/liquid-forge/services/templates/sync/template-dev-synchronizer.ts +++ b/packages/liquid-forge/services/templates/sync/template-dev-synchronizer.ts @@ -17,6 +17,7 @@ import { logger } from '../../../lib/logger'; import { cacheManager } from '../../core/cache'; import { cacheInvalidationService } from '../../core/cache/cache-invalidation-service'; +import { PATH_PATTERNS } from '../../../lib/regex-patterns'; import { templateLoader } from '../template-loader'; import { PostCSSProcessor } from '../../themes/optimization/postcss-processor'; import { getContentType, isBinaryFile } from '@/lib/utils/file-utils'; @@ -190,17 +191,12 @@ export class TemplateDevSynchronizer { throw new Error(`Unsafe path: ${filePath}`); } const validatedPath = path.resolve(filePath); - const relativePath = path.relative(this.localDir, validatedPath).replace(/\\/g, '/'); + const relativePath = path.relative(this.localDir, validatedPath).replace(PATH_PATTERNS.backslashes, '/'); return { validatedPath, relativePath }; } - /** - * Construye la clave de S3 para un archivo relativo al directorio local. - * @param relativePath - Ruta relativa desde el directorio local - * @returns Clave S3 normalizada - */ private buildS3Key(relativePath: string): string { - return `templates/${this.storeId}/${relativePath}`.replace(/\\/g, '/'); + return `templates/${this.storeId}/${relativePath}`.replace(PATH_PATTERNS.backslashes, '/'); } /** @@ -240,7 +236,7 @@ export class TemplateDevSynchronizer { await this.deleteFileFromS3(s3Key); } - const templateName = relativePath.replace(/\\/g, '/'); + const templateName = relativePath.replace(PATH_PATTERNS.backslashes, '/'); templateLoader.invalidateTemplateCache(this.storeId, templateName); @@ -275,7 +271,7 @@ export class TemplateDevSynchronizer { const isBinary = isBinaryFile(validatedPath); - const relativePath = path.relative(this.localDir, validatedPath).replace(/\\/g, '/'); + const relativePath = path.relative(this.localDir, validatedPath).replace(PATH_PATTERNS.backslashes, '/'); let body; if (isBinary) { diff --git a/packages/liquid-forge/services/themes/core/theme-processor.ts b/packages/liquid-forge/services/themes/core/theme-processor.ts index 2821f660..0c0fc0f7 100644 --- a/packages/liquid-forge/services/themes/core/theme-processor.ts +++ b/packages/liquid-forge/services/themes/core/theme-processor.ts @@ -18,6 +18,7 @@ import { TemplateAnalysis } from '../../../exports'; import { RendererLogger } from '../../../lib/logger'; import { SchemaParser } from '../../templates/parsing/schema-parser'; import { PostCSSProcessor } from '../optimization/postcss-processor'; +import { SANITIZATION_PATTERNS } from '../../../lib/regex-patterns'; import JSZip from 'jszip'; import type { ProcessedTheme, @@ -407,7 +408,7 @@ export class ThemeProcessor { */ private generateThemeId(name: string, storeId: string): string { const timestamp = Date.now(); - const sanitizedName = name.toLowerCase().replace(/[^a-z0-9]/g, '-'); + const sanitizedName = name.toLowerCase().replace(SANITIZATION_PATTERNS.themeName, '-'); return `${sanitizedName}-${storeId}-${timestamp}`; } diff --git a/packages/liquid-forge/services/themes/optimization/postcss-processor.ts b/packages/liquid-forge/services/themes/optimization/postcss-processor.ts index a7113eff..aea7f913 100644 --- a/packages/liquid-forge/services/themes/optimization/postcss-processor.ts +++ b/packages/liquid-forge/services/themes/optimization/postcss-processor.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { MINIFICATION_PATTERNS, CSS_MINIFICATION_PATTERNS } from '../../../lib/regex-patterns'; + export interface PostCSSOptions { autoprefixer: boolean; removeComments: boolean; @@ -65,14 +67,13 @@ export class PostCSSProcessor { this.cssProcessor = null; } - // Para JS usamos minificación simple (como hacen los desarrolladores de Shopify) this.jsProcessor = { minify: (js: string): string => { return js - .replace(/\/\/.*$/gm, '') // Comentarios de línea - .replace(/\/\*[\s\S]*?\*\//g, '') // Comentarios de bloque - .replace(/\s+/g, ' ') // Espacios - .replace(/\s*([=+\-*/%&|^!~?:,;{}()\[\]<>])\s*/g, '$1') + .replace(MINIFICATION_PATTERNS.lineComment, '') + .replace(MINIFICATION_PATTERNS.blockComment, '') + .replace(MINIFICATION_PATTERNS.multipleSpaces, ' ') + .replace(MINIFICATION_PATTERNS.operatorSpaces, '$1') .trim(); }, }; diff --git a/packages/liquid-forge/services/themes/validation/rules/performance-rules.ts b/packages/liquid-forge/services/themes/validation/rules/performance-rules.ts index f22d17dd..635e7962 100644 --- a/packages/liquid-forge/services/themes/validation/rules/performance-rules.ts +++ b/packages/liquid-forge/services/themes/validation/rules/performance-rules.ts @@ -22,6 +22,7 @@ import type { ValidationWarning, } from '../../types'; import { filterAssetFiles, filterTextFiles } from '../utils/file-filters'; +import { SECURITY_PATTERNS } from '../../../../lib/regex-patterns'; export class PerformanceRules { /** @@ -139,10 +140,9 @@ export class PerformanceRules { for (const file of textFiles) { const content = file.content as string; - // Contar recursos externos - const externalScripts = (content.match(/]*src=["'](https?:\/\/[^"']+)["'][^>]*>/gi) || []).length; - const externalCss = (content.match(/]*href=["'](https?:\/\/[^"']+)["'][^>]*>/gi) || []).length; - const externalImages = (content.match(/src=["'](https?:\/\/[^"']+)["']/gi) || []).length; + const externalScripts = (content.match(SECURITY_PATTERNS.externalScript) || []).length; + const externalCss = (content.match(SECURITY_PATTERNS.externalCss) || []).length; + const externalImages = (content.match(SECURITY_PATTERNS.externalImage) || []).length; if (externalScripts > 5) { warnings.push({ diff --git a/packages/liquid-forge/services/themes/validation/rules/security-rules.ts b/packages/liquid-forge/services/themes/validation/rules/security-rules.ts index 701c6334..0625eefd 100644 --- a/packages/liquid-forge/services/themes/validation/rules/security-rules.ts +++ b/packages/liquid-forge/services/themes/validation/rules/security-rules.ts @@ -22,6 +22,11 @@ import type { ValidationWarning, } from '../../types'; import { filterTextFiles } from '../utils/file-filters'; +import { + SECURITY_PATTERNS, + DANGEROUS_FUNCTION_PATTERNS, + SENSITIVE_DATA_PATTERNS, +} from '../../../../lib/regex-patterns'; export class SecurityRules { /** @@ -49,8 +54,7 @@ export class SecurityRules { } } - // Verificar scripts externos - const externalScriptMatches = content.match(/]*src=["'](https?:\/\/[^"']+)["'][^>]*>/gi); + const externalScriptMatches = content.match(SECURITY_PATTERNS.externalScript); if (externalScriptMatches) { warnings.push({ type: 'security', @@ -60,8 +64,7 @@ export class SecurityRules { }); } - // Verificar CSS externo - const externalCssMatches = content.match(/]*href=["'](https?:\/\/[^"']+)["'][^>]*>/gi); + const externalCssMatches = content.match(SECURITY_PATTERNS.externalCss); if (externalCssMatches) { warnings.push({ type: 'security', @@ -83,14 +86,14 @@ export class SecurityRules { const warnings: ValidationWarning[] = []; const dangerousPatterns = [ - /eval\s*\(/, - /Function\s*\(/, - /setTimeout\s*\(/, - /setInterval\s*\(/, - /document\.write/, - /document\.writeln/, - /innerHTML\s*=/, - /outerHTML\s*=/, + DANGEROUS_FUNCTION_PATTERNS.eval, + DANGEROUS_FUNCTION_PATTERNS.functionConstructor, + DANGEROUS_FUNCTION_PATTERNS.setTimeout, + DANGEROUS_FUNCTION_PATTERNS.setInterval, + DANGEROUS_FUNCTION_PATTERNS.documentWrite, + DANGEROUS_FUNCTION_PATTERNS.documentWriteln, + DANGEROUS_FUNCTION_PATTERNS.innerHTML, + DANGEROUS_FUNCTION_PATTERNS.outerHTML, ]; // Filtrar solo archivos basados en texto @@ -127,8 +130,7 @@ export class SecurityRules { for (const file of textFiles) { const content = file.content as string; - // Buscar URLs externas - const urlMatches = content.match(/https?:\/\/[^\s"']+/gi); + const urlMatches = content.match(SECURITY_PATTERNS.externalUrl); if (urlMatches) { for (const url of urlMatches) { // Verificar si es un dominio sospechoso @@ -154,7 +156,14 @@ export class SecurityRules { const errors: ValidationError[] = []; const warnings: ValidationWarning[] = []; - const sensitivePatterns = [/api_key/, /secret/, /password/, /token/, /private_key/, /access_key/]; + const sensitivePatterns = [ + SENSITIVE_DATA_PATTERNS.apiKey, + SENSITIVE_DATA_PATTERNS.secret, + SENSITIVE_DATA_PATTERNS.password, + SENSITIVE_DATA_PATTERNS.token, + SENSITIVE_DATA_PATTERNS.privateKey, + SENSITIVE_DATA_PATTERNS.accessKey, + ]; // Filtrar solo archivos basados en texto const textFiles = filterTextFiles(files); diff --git a/test/unit/liquid-tags/render-tag.test.ts b/test/unit/liquid-tags/render-tag.test.ts index ff86e4e7..18984c88 100644 --- a/test/unit/liquid-tags/render-tag.test.ts +++ b/test/unit/liquid-tags/render-tag.test.ts @@ -3,10 +3,11 @@ import { beforeEach, describe, expect, it, jest } from '@jest/globals'; import { Liquid } from 'liquidjs'; import { createTestContext, createTestLiquid, mockTemplateLoader } from './setup'; -// Mock del TemplateLoader +const mockGetInstance = jest.fn(() => mockTemplateLoader); + jest.mock('@fasttify/liquid-forge/services/templates/template-loader', () => ({ TemplateLoader: { - getInstance: () => mockTemplateLoader, + getInstance: mockGetInstance, }, })); @@ -18,7 +19,9 @@ describe('RenderTag', () => { liquid.registerTag('render', RenderTag); // Reset mocks - jest.clearAllMocks(); + mockGetInstance.mockClear(); + mockTemplateLoader.loadTemplate.mockClear(); + mockTemplateLoader.loadTemplate.mockResolvedValue('
Mock Template
'); }); it('should render a basic snippet', async () => {