@ecomiq/storefront 2.0.2 → 2.0.5

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 CHANGED
@@ -54,4 +54,14 @@ npx ecomiq clean
54
54
  npx ecomiq dev
55
55
  ```
56
56
 
57
+ ### Runtime de desarrollo
58
+
59
+ `ecomiq dev` usa **Node** por defecto. Para emular Workers en local:
60
+
61
+ ```js
62
+ dev: { port: 3000, runtime: 'workerd' },
63
+ ```
64
+
65
+ `ecomiq build` / `deploy` siempre empaquetan para Cloudflare Workers.
66
+
57
67
  Ver el monorepo [ecomiq-storefront](https://github.com/ecomiq/ecomiq-storefront) para la app de referencia y más documentación.
package/bin/ecomiq.mjs CHANGED
@@ -16,7 +16,7 @@ import { resolve, dirname } from 'node:path'
16
16
  import { fileURLToPath, pathToFileURL } from 'node:url'
17
17
  import { generateApp, watchPages } from '../src/codegen.mjs'
18
18
  import { parseConfig } from '../src/config.mjs'
19
- import { createViteConfig } from '../src/vite-inline.mjs'
19
+ import { createViteConfig, resolveDevRuntime } from '../src/vite-inline.mjs'
20
20
 
21
21
  const __dirname = dirname(fileURLToPath(import.meta.url))
22
22
  const cwd = process.cwd()
@@ -80,9 +80,18 @@ export async function runCli(command = process.argv[2] ?? 'help') {
80
80
  case 'dev': {
81
81
  const config = await prepare()
82
82
  const port = config.dev?.port ?? 3000
83
- banner(`dev · ${config.name ?? 'tienda'} ${c.dim('http://localhost:' + port)}`)
83
+ const runtime = resolveDevRuntime({ config })
84
84
  const { createServer } = await import('vite')
85
- const viteConfig = createViteConfig({ appDir, config, projectRoot: cwd })
85
+ const viteConfig = createViteConfig({
86
+ appDir,
87
+ config,
88
+ projectRoot: cwd,
89
+ runtime,
90
+ })
91
+ banner(
92
+ `dev · ${config.name ?? 'tienda'} ${c.dim('http://localhost:' + port)}` +
93
+ (runtime === 'workerd' ? c.dim(' · runtime workerd') : ''),
94
+ )
86
95
  const server = await createServer({
87
96
  ...viteConfig,
88
97
  server: {
@@ -115,7 +124,9 @@ export async function runCli(command = process.argv[2] ?? 'help') {
115
124
  const config = await prepare()
116
125
  banner(`build · Cloudflare Workers`)
117
126
  const { createBuilder } = await import('vite')
118
- const builder = await createBuilder(createViteConfig({ appDir, config, projectRoot: cwd }))
127
+ const builder = await createBuilder(
128
+ createViteConfig({ appDir, config, projectRoot: cwd, runtime: 'workerd' }),
129
+ )
119
130
  await builder.buildApp()
120
131
  console.log(c.green('\n ✔ Build listo en .ecomiq/dist\n'))
121
132
  break
@@ -124,7 +135,9 @@ export async function runCli(command = process.argv[2] ?? 'help') {
124
135
  const config = await prepare()
125
136
  banner(`deploy · Cloudflare Workers`)
126
137
  const { createBuilder } = await import('vite')
127
- const builder = await createBuilder(createViteConfig({ appDir, config, projectRoot: cwd }))
138
+ const builder = await createBuilder(
139
+ createViteConfig({ appDir, config, projectRoot: cwd, runtime: 'workerd' }),
140
+ )
128
141
  await builder.buildApp()
129
142
  // @cloudflare/vite-plugin emite la config final del Worker junto al build
130
143
  const built = resolve(appDir, 'dist', 'server', 'wrangler.json')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecomiq/storefront",
3
- "version": "2.0.2",
3
+ "version": "2.0.5",
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",
package/src/config.d.ts CHANGED
@@ -43,6 +43,8 @@ export interface EcomiqConfigInput {
43
43
  dev?: {
44
44
  port?: number
45
45
  apiBaseUrl?: string
46
+ /** Default `node`. `workerd` emula Cloudflare en local. */
47
+ runtime?: 'workerd' | 'node'
46
48
  }
47
49
  deploy?: {
48
50
  target?: 'cloudflare'
package/src/config.mjs CHANGED
@@ -124,6 +124,12 @@ const configSchema = z.object({
124
124
  .object({
125
125
  port: z.number().int().default(3000),
126
126
  apiBaseUrl: z.string().url(),
127
+ /**
128
+ * Runtime SSR en `ecomiq dev`. Default: `node`.
129
+ * Usa `workerd` para emular Cloudflare Workers en local.
130
+ * `ecomiq build` / `deploy` siempre usan workerd.
131
+ */
132
+ runtime: z.enum(['workerd', 'node']),
127
133
  })
128
134
  .partial()
129
135
  .optional(),
@@ -3,6 +3,9 @@
3
3
  * No existe vite.config.ts en el proyecto del usuario ni en .ecomiq:
4
4
  * los plugins se importan aquí (contexto del framework) y se pasan como
5
5
  * instancias, así la resolución de módulos nunca depende del proyecto.
6
+ *
7
+ * Alineado con el ejemplo oficial TanStack Start + Cloudflare:
8
+ * https://github.com/TanStack/router/tree/main/examples/react/start-basic-cloudflare
6
9
  */
7
10
  import { dirname, resolve } from 'node:path'
8
11
  import { fileURLToPath } from 'node:url'
@@ -52,7 +55,39 @@ function sanitizeHtmlClientBoundary() {
52
55
  }
53
56
  }
54
57
 
55
- export function createViteConfig({ appDir, config, projectRoot }) {
58
+ /**
59
+ * TanStack Start marca `@tanstack/react-router` como `resolve.external` fuera de
60
+ * su monorepo. Cloudflare Vite plugin no lo tolera y workerd acaba ejecutando
61
+ * CJS con `require` → "require is not defined".
62
+ * @see https://github.com/TanStack/router/issues/5291
63
+ */
64
+ function clearSsrExternal() {
65
+ return {
66
+ name: 'ecomiq:clear-ssr-external',
67
+ configResolved(config) {
68
+ const ssr = config.environments?.ssr
69
+ if (ssr?.resolve) {
70
+ ssr.resolve.external = []
71
+ ssr.resolve.noExternal = true
72
+ }
73
+ },
74
+ }
75
+ }
76
+
77
+ /**
78
+ * @param {{ appDir: string, config?: object, projectRoot?: string, runtime?: 'workerd' | 'node' }} opts
79
+ */
80
+ export function resolveDevRuntime({ config, runtime } = {}) {
81
+ // Default: Node (compatible con npm/CodeSandbox). Opt-in a workerd con
82
+ // `dev.runtime: 'workerd'` (p. ej. monorepo local). build/deploy siempre
83
+ // fuerza workerd desde la CLI.
84
+ return runtime ?? config?.dev?.runtime ?? 'node'
85
+ }
86
+
87
+ /**
88
+ * @param {{ appDir: string, config?: object, projectRoot?: string, runtime?: 'workerd' | 'node' }} opts
89
+ */
90
+ export function createViteConfig({ appDir, config, projectRoot, runtime }) {
56
91
  const root = projectRoot ?? resolve(appDir, '..')
57
92
  const allow = [
58
93
  '..',
@@ -62,6 +97,36 @@ export function createViteConfig({ appDir, config, projectRoot }) {
62
97
  resolve(root, 'node_modules'),
63
98
  ]
64
99
 
100
+ const resolvedRuntime = resolveDevRuntime({ config, runtime })
101
+
102
+ const plugins = [
103
+ clearSsrExternal(),
104
+ tailwindcss(),
105
+ ]
106
+
107
+ // workerd emula Cloudflare Workers (opt-in en `ecomiq dev`).
108
+ // Default es Node; build/deploy siempre usan workerd.
109
+ if (resolvedRuntime === 'workerd') {
110
+ plugins.push(
111
+ cloudflare({
112
+ configPath: resolve(appDir, 'wrangler.jsonc'),
113
+ viteEnvironment: { name: 'ssr' },
114
+ }),
115
+ )
116
+ }
117
+
118
+ plugins.push(
119
+ tanstackStart({
120
+ srcDirectory: 'src',
121
+ router: {
122
+ routesDirectory: 'routes',
123
+ generatedRouteTree: 'routeTree.gen.ts',
124
+ },
125
+ }),
126
+ sanitizeHtmlClientBoundary(),
127
+ viteReact(),
128
+ )
129
+
65
130
  return {
66
131
  root: appDir,
67
132
  configFile: false,
@@ -75,45 +140,8 @@ export function createViteConfig({ appDir, config, projectRoot }) {
75
140
  '@tanstack/react-query',
76
141
  ],
77
142
  },
78
- // Evita que Vite externalice TanStack como file:// absoluto (rompe workerd/pnpm).
79
- ssr: {
80
- noExternal: [
81
- '@tanstack/react-start',
82
- '@tanstack/react-router',
83
- '@tanstack/react-query',
84
- '@tanstack/router-core',
85
- '@tanstack/history',
86
- '@tanstack/react-store',
87
- '@ecomiq/storefront',
88
- '@ecomiq/core',
89
- ],
90
- },
91
- optimizeDeps: {
92
- include: [
93
- '@tanstack/react-start',
94
- '@tanstack/react-router',
95
- '@tanstack/react-query',
96
- ],
97
- },
98
- plugins: [
99
- tailwindcss(),
100
- // Emula el runtime de Workers en dev y empaqueta para Cloudflare en build.
101
- cloudflare({
102
- configPath: resolve(appDir, 'wrangler.jsonc'),
103
- viteEnvironment: { name: 'ssr' },
104
- }),
105
- tanstackStart({
106
- srcDirectory: 'src',
107
- router: {
108
- routesDirectory: 'routes',
109
- generatedRouteTree: 'routeTree.gen.ts',
110
- },
111
- }),
112
- sanitizeHtmlClientBoundary(),
113
- viteReact(),
114
- ],
143
+ plugins,
115
144
  build: {
116
- // Un solo CSS; nombre estable → SSR y client apuntan al mismo asset en Workers.
117
145
  cssCodeSplit: false,
118
146
  rollupOptions: {
119
147
  output: {
@@ -126,10 +154,7 @@ export function createViteConfig({ appDir, config, projectRoot }) {
126
154
  },
127
155
  },
128
156
  server: {
129
- fs: {
130
- // pages/, lib/ y public/ del usuario viven un nivel arriba de .ecomiq
131
- allow,
132
- },
157
+ fs: { allow },
133
158
  },
134
159
  }
135
160
  }