@ecomiq/storefront 2.1.3 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -28
- package/package.json +2 -7
- package/src/codegen.mjs +12 -0
- package/src/index.ts +0 -5
- package/src/pages/pages.$pageSlug.tsx +31 -68
- package/src/server.ts +0 -15
- package/src/styles.css +0 -21
- package/src/cloud-runtime-client.ts +0 -378
- package/src/storefront-definition/contract.ts +0 -1
- package/src/storefront-definition/data.ts +0 -188
- package/src/storefront-definition/index.ts +0 -2
- package/src/storefront-definition/renderer.tsx +0 -1481
- package/src/storefront-layout.ts +0 -59
- package/src/storefront-runtime.ts +0 -173
package/README.md
CHANGED
|
@@ -72,16 +72,14 @@ Ver el monorepo [ecomiq-storefront](https://github.com/ecomiq/ecomiq-storefront)
|
|
|
72
72
|
|
|
73
73
|
## Storefront declarativo
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
El contrato y el renderer viven fuera de este package:
|
|
76
|
+
|
|
77
|
+
- `@ecomiq/storefront-definition` — schema Zod, patches, migraciones
|
|
78
|
+
- `@ecomiq/storefront-definition/react` — `DefinitionRenderer`, loaders y hooks
|
|
79
79
|
|
|
80
80
|
```tsx
|
|
81
|
-
import {
|
|
82
|
-
|
|
83
|
-
createDefaultStorefrontDefinition,
|
|
84
|
-
} from '@ecomiq/storefront'
|
|
81
|
+
import { createDefaultStorefrontDefinition } from '@ecomiq/storefront-definition'
|
|
82
|
+
import { DefinitionRenderer } from '@ecomiq/storefront-definition/react'
|
|
85
83
|
|
|
86
84
|
const definition = createDefaultStorefrontDefinition({
|
|
87
85
|
storeName: 'Casa Lumbre',
|
|
@@ -90,7 +88,7 @@ const definition = createDefaultStorefrontDefinition({
|
|
|
90
88
|
|
|
91
89
|
export function Home() {
|
|
92
90
|
return (
|
|
93
|
-
<
|
|
91
|
+
<DefinitionRenderer
|
|
94
92
|
definition={definition}
|
|
95
93
|
locale="es-PE"
|
|
96
94
|
currency="PEN"
|
|
@@ -107,27 +105,9 @@ export function Home() {
|
|
|
107
105
|
}
|
|
108
106
|
```
|
|
109
107
|
|
|
110
|
-
Los sistemas que solo necesitan validar o modificar el contrato pueden importar
|
|
111
|
-
el subpath sin cargar el renderer:
|
|
112
|
-
|
|
113
108
|
```ts
|
|
114
109
|
import {
|
|
115
110
|
StorefrontDefinitionSchema,
|
|
116
|
-
StorefrontDefinitionPatchSchema,
|
|
117
111
|
applyStorefrontDefinitionPatch,
|
|
118
|
-
|
|
119
|
-
type StorefrontDefinitionPatchInput,
|
|
120
|
-
} from '@ecomiq/storefront/definition'
|
|
112
|
+
} from '@ecomiq/storefront-definition'
|
|
121
113
|
```
|
|
122
|
-
|
|
123
|
-
Cada definición lleva `version` y `revision`. Los patches son puros, exigen
|
|
124
|
-
`expectedRevision` y conservan el ID de una sección al reemplazarla. Soportan
|
|
125
|
-
`section.add`, `section.replace`, `section.remove`, `section.reorder`,
|
|
126
|
-
`theme.replace`, `navigation.replace` y `footer.replace`.
|
|
127
|
-
|
|
128
|
-
El template productivo consulta server-side
|
|
129
|
-
`GET /v1/runtime/storefronts/:storeSlug`, revalida la publicación mediante
|
|
130
|
-
ETag y resuelve los `product-grid.source` y `collection-grid.source` contra el
|
|
131
|
-
API storefront. La publicación declarativa reemplaza solo el contenido de la
|
|
132
|
-
home; el layout comercial y las rutas de catálogo, producto, carrito, checkout
|
|
133
|
-
y cuenta siguen siendo las del framework.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecomiq/storefront",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.4",
|
|
4
4
|
"description": "Micro framework SSR de Ecomiq para storefronts. Páginas, datos y deploy sin configurar nada.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,14 +32,10 @@
|
|
|
32
32
|
"default": "./src/config.mjs"
|
|
33
33
|
},
|
|
34
34
|
"./ui": "./src/ui/index.ts",
|
|
35
|
-
"./definition": "./src/storefront-definition/contract.ts",
|
|
36
|
-
"./definition/react": "./src/storefront-definition/index.ts",
|
|
37
35
|
"./styles.css": "./src/styles.css",
|
|
38
36
|
"./internal/page": "./src/runtime/page.tsx",
|
|
39
37
|
"./internal/context": "./src/runtime/context.ts",
|
|
40
38
|
"./internal/server-config": "./src/server-config-store.ts",
|
|
41
|
-
"./internal/runtime": "./src/storefront-runtime.ts",
|
|
42
|
-
"./internal/definition-data": "./src/storefront-definition/data.ts",
|
|
43
39
|
"./internal/health": "./src/health.ts",
|
|
44
40
|
"./internal/head": "./src/runtime/head.ts",
|
|
45
41
|
"./internal/default-layout": "./src/runtime/default-layout.tsx",
|
|
@@ -86,8 +82,7 @@
|
|
|
86
82
|
"vite": "^8.1.0",
|
|
87
83
|
"wrangler": "^4.100.0",
|
|
88
84
|
"zod": "^3.25.0",
|
|
89
|
-
"@ecomiq/core": "^0.1.9"
|
|
90
|
-
"@ecomiq/storefront-definition": "^1.0.0"
|
|
85
|
+
"@ecomiq/core": "^0.1.9"
|
|
91
86
|
},
|
|
92
87
|
"peerDependencies": {
|
|
93
88
|
"react": "^19.0.0",
|
package/src/codegen.mjs
CHANGED
|
@@ -198,10 +198,22 @@ export async function generateApp({ cwd, appDir, frameworkRoot, config }) {
|
|
|
198
198
|
|
|
199
199
|
// ── 5. styles.css: tokens del framework + fuentes de contenido del usuario ──
|
|
200
200
|
const themeOverrides = buildThemeOverrides(config)
|
|
201
|
+
const projectPkgPath = resolve(cwd, 'package.json')
|
|
202
|
+
const projectPkg = existsSync(projectPkgPath)
|
|
203
|
+
? JSON.parse(readFileSync(projectPkgPath, 'utf8'))
|
|
204
|
+
: {}
|
|
205
|
+
const hasStorefrontDefinition = Boolean(
|
|
206
|
+
projectPkg.dependencies?.['@ecomiq/storefront-definition'] ||
|
|
207
|
+
projectPkg.devDependencies?.['@ecomiq/storefront-definition'],
|
|
208
|
+
)
|
|
201
209
|
writeGeneratedFile(
|
|
202
210
|
join(srcDir, 'styles.css'),
|
|
203
211
|
HEADER.replace('/*', '/*!') +
|
|
204
212
|
`@import '@ecomiq/storefront/styles.css';\n` +
|
|
213
|
+
(hasStorefrontDefinition
|
|
214
|
+
? `@import '@ecomiq/storefront-definition/react/styles.css';\n` +
|
|
215
|
+
`@source '../../../node_modules/@ecomiq/storefront-definition/src/react';\n`
|
|
216
|
+
: '') +
|
|
205
217
|
`@source '../../pages';\n` +
|
|
206
218
|
`@source '../../components';\n` +
|
|
207
219
|
`@source '../../lib';\n` +
|
package/src/index.ts
CHANGED
|
@@ -246,11 +246,6 @@ export {
|
|
|
246
246
|
type HeroProps,
|
|
247
247
|
type HeroSlide,
|
|
248
248
|
} from './components/hero'
|
|
249
|
-
export * from './storefront-definition'
|
|
250
|
-
export {
|
|
251
|
-
definitionForRoute,
|
|
252
|
-
useDefinition,
|
|
253
|
-
} from './storefront-layout'
|
|
254
249
|
export { z } from 'zod'
|
|
255
250
|
|
|
256
251
|
/**
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Página de contenido (`/pages/$pageSlug`).
|
|
3
|
-
*
|
|
4
|
-
* de lectura para tiendas que aún no migraron sus páginas.
|
|
2
|
+
* Página de contenido CMS (`/pages/$pageSlug`).
|
|
3
|
+
* Las páginas Definition las sirve el template (`$.tsx`).
|
|
5
4
|
*/
|
|
6
5
|
import type { CmsPageDetailsDto } from '@ecomiq/core'
|
|
7
|
-
import type { ActiveStorefrontPage } from '../storefront-runtime'
|
|
8
6
|
import {
|
|
9
|
-
DefinitionSections,
|
|
10
7
|
definePage,
|
|
11
8
|
sanitizeHtml,
|
|
12
9
|
serverApi,
|
|
@@ -24,51 +21,30 @@ const ContentPageSettingsSchema = z.object({
|
|
|
24
21
|
}).optional(),
|
|
25
22
|
}).passthrough()
|
|
26
23
|
|
|
27
|
-
type ContentPageData =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
page: CmsPageDetailsDto
|
|
32
|
-
content: z.infer<typeof ContentPageSettingsSchema>
|
|
33
|
-
}
|
|
24
|
+
type ContentPageData = {
|
|
25
|
+
page: CmsPageDetailsDto
|
|
26
|
+
content: z.infer<typeof ContentPageSettingsSchema>
|
|
27
|
+
}
|
|
34
28
|
|
|
35
29
|
export default definePage<ContentPageData, { pageSlug: string }>({
|
|
36
30
|
loader: async ({ params }) => {
|
|
37
|
-
const resolved = await serverApi.getActiveStorefrontPage(
|
|
38
|
-
`/pages/${params.pageSlug}`,
|
|
39
|
-
)
|
|
40
|
-
if (resolved.kind === 'definition') return resolved
|
|
41
|
-
|
|
42
31
|
const page = await serverApi.getPageBySlug(params.pageSlug)
|
|
43
32
|
if (page.status !== 'published') throw new Error('Página no publicada')
|
|
44
33
|
|
|
45
34
|
return {
|
|
46
|
-
kind: 'cms' as const,
|
|
47
35
|
page,
|
|
48
36
|
content: ContentPageSettingsSchema.parse(page.settings ?? {}),
|
|
49
37
|
}
|
|
50
38
|
},
|
|
51
39
|
|
|
52
|
-
meta: ({ data
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
...(page.seo.noindex ? { noIndex: true } : {}),
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return {
|
|
64
|
-
title: data.content.seo?.title ?? data.content.title ?? data.page.name,
|
|
65
|
-
description: data.content.seo?.description ?? (
|
|
66
|
-
data.content.html
|
|
67
|
-
? stripHtml(data.content.html).slice(0, 155)
|
|
68
|
-
: undefined
|
|
69
|
-
),
|
|
70
|
-
}
|
|
71
|
-
},
|
|
40
|
+
meta: ({ data }) => ({
|
|
41
|
+
title: data.content.seo?.title ?? data.content.title ?? data.page.name,
|
|
42
|
+
description: data.content.seo?.description ?? (
|
|
43
|
+
data.content.html
|
|
44
|
+
? stripHtml(data.content.html).slice(0, 155)
|
|
45
|
+
: undefined
|
|
46
|
+
),
|
|
47
|
+
}),
|
|
72
48
|
|
|
73
49
|
error: () => (
|
|
74
50
|
<StorefrontError
|
|
@@ -77,35 +53,22 @@ export default definePage<ContentPageData, { pageSlug: string }>({
|
|
|
77
53
|
/>
|
|
78
54
|
),
|
|
79
55
|
|
|
80
|
-
component: ({ data
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
locale={settings.store.language}
|
|
88
|
-
currency={settings.store.currency}
|
|
89
|
-
/>
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
return (
|
|
93
|
-
<article className="mx-auto w-full max-w-4xl px-4 py-10 sm:py-14">
|
|
94
|
-
<header className="mb-8 border-b pb-8">
|
|
95
|
-
<h1 className="text-balance text-3xl font-semibold tracking-[-0.02em] md:text-4xl">
|
|
96
|
-
{data.content.title ?? data.page.name}
|
|
97
|
-
</h1>
|
|
98
|
-
</header>
|
|
56
|
+
component: ({ data }) => (
|
|
57
|
+
<article className="mx-auto w-full max-w-4xl px-4 py-10 sm:py-14">
|
|
58
|
+
<header className="mb-8 border-b pb-8">
|
|
59
|
+
<h1 className="text-balance text-3xl font-semibold tracking-[-0.02em] md:text-4xl">
|
|
60
|
+
{data.content.title ?? data.page.name}
|
|
61
|
+
</h1>
|
|
62
|
+
</header>
|
|
99
63
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
},
|
|
64
|
+
{data.content.html ? (
|
|
65
|
+
<div
|
|
66
|
+
className="prose prose-neutral max-w-none text-foreground prose-headings:font-semibold prose-a:text-primary"
|
|
67
|
+
dangerouslySetInnerHTML={{ __html: sanitizeHtml(data.content.html) }}
|
|
68
|
+
/>
|
|
69
|
+
) : (
|
|
70
|
+
<p className="text-muted-foreground">Contenido no disponible.</p>
|
|
71
|
+
)}
|
|
72
|
+
</article>
|
|
73
|
+
),
|
|
111
74
|
})
|
package/src/server.ts
CHANGED
|
@@ -71,23 +71,11 @@ import {
|
|
|
71
71
|
type AddProductToCartInput,
|
|
72
72
|
} from './cart-session'
|
|
73
73
|
import { submitProductReview } from './review-submission'
|
|
74
|
-
import {
|
|
75
|
-
loadActiveStorefrontHome,
|
|
76
|
-
loadActiveStorefrontPage,
|
|
77
|
-
} from './storefront-runtime'
|
|
78
74
|
|
|
79
75
|
const getSettingsFn = createServerFn({ method: 'GET' }).handler(() =>
|
|
80
76
|
getStorefrontClient().getSettings(),
|
|
81
77
|
)
|
|
82
78
|
|
|
83
|
-
const getActiveStorefrontHomeFn = createServerFn({ method: 'GET' }).handler(
|
|
84
|
-
() => loadActiveStorefrontHome(),
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
const getActiveStorefrontPageFn = createServerFn({ method: 'GET' })
|
|
88
|
-
.validator(z.object({ pathname: z.string().min(1).max(2_048) }))
|
|
89
|
-
.handler(({ data }) => loadActiveStorefrontPage(data.pathname))
|
|
90
|
-
|
|
91
79
|
const getStoreIdFn = createServerFn({ method: 'GET' }).handler(() =>
|
|
92
80
|
getStorefrontClient().getStoreId(),
|
|
93
81
|
)
|
|
@@ -234,9 +222,6 @@ const getDistrictsFn = createServerFn({ method: 'GET' })
|
|
|
234
222
|
*/
|
|
235
223
|
export const serverApi = {
|
|
236
224
|
getSettings: () => getSettingsFn(),
|
|
237
|
-
getActiveStorefrontHome: () => getActiveStorefrontHomeFn(),
|
|
238
|
-
getActiveStorefrontPage: (pathname: string) =>
|
|
239
|
-
getActiveStorefrontPageFn({ data: { pathname } }),
|
|
240
225
|
getStoreId: () => getStoreIdFn(),
|
|
241
226
|
getCollections: () => getCollectionsFn(),
|
|
242
227
|
getCollectionBySlug: (slug: string) => getCollectionBySlugFn({ data: { slug } }),
|
package/src/styles.css
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
/* Escanear los componentes UI del propio framework */
|
|
6
6
|
@source './ui';
|
|
7
7
|
@source './components';
|
|
8
|
-
@source './storefront-definition';
|
|
9
8
|
@source './runtime';
|
|
10
9
|
@source './pages';
|
|
11
10
|
@source '../templates';
|
|
@@ -75,23 +74,3 @@
|
|
|
75
74
|
transition-duration: 0.01ms !important;
|
|
76
75
|
}
|
|
77
76
|
}
|
|
78
|
-
|
|
79
|
-
.storefront-definition {
|
|
80
|
-
font-family: var(--storefront-font-body);
|
|
81
|
-
--storefront-content-width: 72rem;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.storefront-definition :where(h1, h2, h3, h4, h5, h6) {
|
|
85
|
-
font-family: var(--storefront-font-heading);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.storefront-definition-section {
|
|
89
|
-
padding-block: var(
|
|
90
|
-
--storefront-section-padding-top,
|
|
91
|
-
var(--storefront-section-padding)
|
|
92
|
-
)
|
|
93
|
-
var(
|
|
94
|
-
--storefront-section-padding-bottom,
|
|
95
|
-
var(--storefront-section-padding)
|
|
96
|
-
);
|
|
97
|
-
}
|