@ecomiq/storefront 2.0.3 → 2.0.7

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,17 @@ 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(`· runtime ${runtime}`)} ${c.dim('http://localhost:' + port)}`,
93
+ )
86
94
  const server = await createServer({
87
95
  ...viteConfig,
88
96
  server: {
@@ -115,7 +123,9 @@ export async function runCli(command = process.argv[2] ?? 'help') {
115
123
  const config = await prepare()
116
124
  banner(`build · Cloudflare Workers`)
117
125
  const { createBuilder } = await import('vite')
118
- const builder = await createBuilder(createViteConfig({ appDir, config, projectRoot: cwd }))
126
+ const builder = await createBuilder(
127
+ createViteConfig({ appDir, config, projectRoot: cwd, runtime: 'workerd' }),
128
+ )
119
129
  await builder.buildApp()
120
130
  console.log(c.green('\n ✔ Build listo en .ecomiq/dist\n'))
121
131
  break
@@ -124,7 +134,9 @@ export async function runCli(command = process.argv[2] ?? 'help') {
124
134
  const config = await prepare()
125
135
  banner(`deploy · Cloudflare Workers`)
126
136
  const { createBuilder } = await import('vite')
127
- const builder = await createBuilder(createViteConfig({ appDir, config, projectRoot: cwd }))
137
+ const builder = await createBuilder(
138
+ createViteConfig({ appDir, config, projectRoot: cwd, runtime: 'workerd' }),
139
+ )
128
140
  await builder.buildApp()
129
141
  // @cloudflare/vite-plugin emite la config final del Worker junto al build
130
142
  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.3",
3
+ "version": "2.0.7",
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'
@@ -17,28 +20,6 @@ const postcssBrowserStub = fileURLToPath(
17
20
 
18
21
  const frameworkRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
19
22
 
20
- /** Packages que workerd debe ver como ESM (sin prebundle CJS con `require`). */
21
- const SSR_NO_EXTERNAL = [
22
- '@tanstack/react-start',
23
- '@tanstack/react-router',
24
- '@tanstack/react-query',
25
- '@tanstack/router-core',
26
- '@tanstack/history',
27
- '@tanstack/react-store',
28
- '@tanstack/start-server-core',
29
- '@tanstack/start-client-core',
30
- '@tanstack/start-storage-context',
31
- '@ecomiq/storefront',
32
- '@ecomiq/core',
33
- ]
34
-
35
- const OPTIMIZE_EXCLUDE = [
36
- '@tanstack/react-start',
37
- '@tanstack/start-server-core',
38
- '@tanstack/start-client-core',
39
- '@tanstack/react-router',
40
- ]
41
-
42
23
  function resolveSanitizeHtmlPostcss(source, importer) {
43
24
  if (source !== 'postcss') return null
44
25
  if (!importer?.includes('/sanitize-html/')) return null
@@ -74,7 +55,52 @@ function sanitizeHtmlClientBoundary() {
74
55
  }
75
56
  }
76
57
 
77
- 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
+ *
63
+ * Solo aplica en runtime `workerd`. En `node`, React y otras deps CJS deben
64
+ * quedar external: `noExternal` las pasa por el module runner ESM de Vite y
65
+ * falla con "module is not defined".
66
+ * @see https://github.com/TanStack/router/issues/5291
67
+ */
68
+ function bundleSsrDependenciesForWorkerd() {
69
+ return {
70
+ name: 'ecomiq:ssr-no-external',
71
+ configEnvironment(name) {
72
+ if (name !== 'ssr') return
73
+ return {
74
+ resolve: {
75
+ external: [],
76
+ noExternal: true,
77
+ },
78
+ }
79
+ },
80
+ configResolved(config) {
81
+ const ssr = config.environments?.ssr
82
+ if (ssr?.resolve) {
83
+ ssr.resolve.external = []
84
+ ssr.resolve.noExternal = true
85
+ }
86
+ },
87
+ }
88
+ }
89
+
90
+ /**
91
+ * @param {{ appDir: string, config?: object, projectRoot?: string, runtime?: 'workerd' | 'node' }} opts
92
+ */
93
+ export function resolveDevRuntime({ config, runtime } = {}) {
94
+ // Default: Node (compatible con npm/CodeSandbox). Opt-in a workerd con
95
+ // `dev.runtime: 'workerd'` (p. ej. monorepo local). build/deploy siempre
96
+ // fuerza workerd desde la CLI.
97
+ return runtime ?? config?.dev?.runtime ?? 'node'
98
+ }
99
+
100
+ /**
101
+ * @param {{ appDir: string, config?: object, projectRoot?: string, runtime?: 'workerd' | 'node' }} opts
102
+ */
103
+ export function createViteConfig({ appDir, config, projectRoot, runtime }) {
78
104
  const root = projectRoot ?? resolve(appDir, '..')
79
105
  const allow = [
80
106
  '..',
@@ -84,6 +110,37 @@ export function createViteConfig({ appDir, config, projectRoot }) {
84
110
  resolve(root, 'node_modules'),
85
111
  ]
86
112
 
113
+ const resolvedRuntime = resolveDevRuntime({ config, runtime })
114
+
115
+ const plugins = [tailwindcss()]
116
+
117
+ if (resolvedRuntime === 'workerd') {
118
+ plugins.unshift(bundleSsrDependenciesForWorkerd())
119
+ }
120
+
121
+ // workerd emula Cloudflare Workers (opt-in en `ecomiq dev`).
122
+ // Default es Node; build/deploy siempre usan workerd.
123
+ if (resolvedRuntime === 'workerd') {
124
+ plugins.push(
125
+ cloudflare({
126
+ configPath: resolve(appDir, 'wrangler.jsonc'),
127
+ viteEnvironment: { name: 'ssr' },
128
+ }),
129
+ )
130
+ }
131
+
132
+ plugins.push(
133
+ tanstackStart({
134
+ srcDirectory: 'src',
135
+ router: {
136
+ routesDirectory: 'routes',
137
+ generatedRouteTree: 'routeTree.gen.ts',
138
+ },
139
+ }),
140
+ sanitizeHtmlClientBoundary(),
141
+ viteReact(),
142
+ )
143
+
87
144
  return {
88
145
  root: appDir,
89
146
  configFile: false,
@@ -97,35 +154,12 @@ export function createViteConfig({ appDir, config, projectRoot }) {
97
154
  '@tanstack/react-query',
98
155
  ],
99
156
  },
100
- // Bundlear TanStack en SSR (no externalizar) evita `file://` de pnpm y
101
- // mantiene ESM. No usar optimizeDeps.include: el prebundle mete `require`
102
- // y workerd (ESM) falla con "require is not defined".
103
- ssr: {
104
- noExternal: [...SSR_NO_EXTERNAL],
105
- },
106
- optimizeDeps: {
107
- exclude: [...OPTIMIZE_EXCLUDE],
108
- },
109
- plugins: [
110
- tailwindcss(),
111
- // Emula el runtime de Workers en dev y empaqueta para Cloudflare en build.
112
- cloudflare({
113
- configPath: resolve(appDir, 'wrangler.jsonc'),
114
- viteEnvironment: { name: 'ssr' },
115
- }),
116
- tanstackStart({
117
- srcDirectory: 'src',
118
- router: {
119
- routesDirectory: 'routes',
120
- generatedRouteTree: 'routeTree.gen.ts',
121
- },
122
- }),
123
- sanitizeHtmlClientBoundary(),
124
- viteReact(),
125
- ],
157
+ plugins,
126
158
  build: {
127
- // Un solo CSS; nombre estable → SSR y client apuntan al mismo asset en Workers.
128
159
  cssCodeSplit: false,
160
+ commonjsOptions: {
161
+ transformMixedEsModules: true,
162
+ },
129
163
  rollupOptions: {
130
164
  output: {
131
165
  assetFileNames: (assetInfo) => {
@@ -137,10 +171,7 @@ export function createViteConfig({ appDir, config, projectRoot }) {
137
171
  },
138
172
  },
139
173
  server: {
140
- fs: {
141
- // pages/, lib/ y public/ del usuario viven un nivel arriba de .ecomiq
142
- allow,
143
- },
174
+ fs: { allow },
144
175
  },
145
176
  }
146
177
  }