create-ecomiq 0.1.0 → 0.1.1

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
@@ -2,6 +2,8 @@
2
2
 
3
3
  Scaffold interactivo de storefronts Ecomiq (estilo `create-vite` / `create-next-app`).
4
4
 
5
+ Usa siempre el template **`scaffold`** (home, catálogo, PDP, carrito, login y registro).
6
+
5
7
  ```bash
6
8
  npm create ecomiq@latest
7
9
  # o
@@ -12,15 +14,13 @@ npx create-ecomiq@latest my-store
12
14
 
13
15
  1. Nombre del proyecto
14
16
  2. **Domain** — se valida con `GET /v1/settings` + `x-Store-Domain`
15
- 3. **Template** catálogo en R2 (`templates.json`); hoy solo `default`
16
- 4. Package manager + install
17
+ 3. Package manager + install
17
18
 
18
19
  ## Flags
19
20
 
20
21
  ```bash
21
22
  create-ecomiq my-store \
22
23
  --domain marketplace \
23
- --template default \
24
24
  --pm pnpm \
25
25
  --yes
26
26
  ```
@@ -28,19 +28,17 @@ create-ecomiq my-store \
28
28
  | Flag | Descripción |
29
29
  | --- | --- |
30
30
  | `--domain` | Domain de la tienda (obligatorio con `--yes`) |
31
- | `--template` | Id del template (`default`) |
32
31
  | `--pm` | `npm` \| `pnpm` \| `yarn` \| `bun` |
33
32
  | `--yes` / `-y` | Sin prompts |
34
33
  | `--skip-install` | Solo archivos |
35
34
 
36
- ## Templates
35
+ ## Template
37
36
 
38
- Los zips viven en R2:
37
+ El zip se descarga desde R2:
39
38
 
40
- - `https://pub-2b5396b516d940dbb1088d750701749c.r2.dev/templates.json`
41
- - `https://pub-2b5396b516d940dbb1088d750701749c.r2.dev/template-{id}.zip`
39
+ `https://pub-2b5396b516d940dbb1088d750701749c.r2.dev/template-scaffold.zip`
42
40
 
43
- Tras el scaffold usa la CLI del framework:
41
+ Tras el scaffold:
44
42
 
45
43
  ```bash
46
44
  cd my-store
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ecomiq",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Scaffold interactivo de storefronts Ecomiq (estilo create-vite)",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/index.mjs CHANGED
@@ -7,7 +7,7 @@
7
7
  import * as p from '@clack/prompts'
8
8
  import { basename, resolve } from 'node:path'
9
9
  import { verifyDomain } from './verify-domain.mjs'
10
- import { downloadTemplateZip, listTemplates } from './templates.mjs'
10
+ import { downloadTemplateZip } from './templates.mjs'
11
11
  import {
12
12
  detectPackageManager,
13
13
  extractTemplateZip,
@@ -17,6 +17,9 @@ import {
17
17
  writeProjectConfig,
18
18
  } from './scaffold.mjs'
19
19
 
20
+ /** Único template que descarga create-ecomiq (CI/CD puede publicar varios). */
21
+ export const CREATE_TEMPLATE_ID = 'scaffold'
22
+
20
23
  /**
21
24
  * @param {string[]} argv
22
25
  */
@@ -24,7 +27,6 @@ export function parseArgs(argv) {
24
27
  /** @type {{
25
28
  * dir?: string
26
29
  * domain?: string
27
- * template?: string
28
30
  * pm?: 'npm' | 'pnpm' | 'yarn' | 'bun'
29
31
  * yes: boolean
30
32
  * skipInstall: boolean
@@ -54,10 +56,6 @@ export function parseArgs(argv) {
54
56
  out.domain = argv[++i]
55
57
  continue
56
58
  }
57
- if (arg === '--template') {
58
- out.template = argv[++i]
59
- continue
60
- }
61
59
  if (arg === '--pm') {
62
60
  const value = argv[++i]
63
61
  if (value === 'npm' || value === 'pnpm' || value === 'yarn' || value === 'bun') {
@@ -83,7 +81,6 @@ function printHelp() {
83
81
 
84
82
  Opciones
85
83
  --domain <slug> Domain de la tienda (x-Store-Domain)
86
- --template <id> Template (default: default)
87
84
  --pm <npm|pnpm|yarn|bun>
88
85
  --yes, -y Sin prompts (requiere --domain)
89
86
  --skip-install No ejecutar install
@@ -92,7 +89,7 @@ function printHelp() {
92
89
  Ejemplos
93
90
  npx create-ecomiq@latest my-store
94
91
  npm create ecomiq@latest
95
- create-ecomiq . --domain marketplace --template default --yes
92
+ create-ecomiq . --domain marketplace --yes
96
93
  `)
97
94
  }
98
95
 
@@ -182,29 +179,7 @@ export async function runCreate(argv) {
182
179
  storeName = result.storeName
183
180
  }
184
181
 
185
- const templates = await listTemplates()
186
- let templateId = args.template?.trim() || 'default'
187
-
188
- if (!args.yes) {
189
- const options = templates.map((tpl) => ({
190
- value: tpl.id,
191
- label: tpl.name,
192
- hint: tpl.description,
193
- }))
194
- const selected = await p.select({
195
- message: 'Elige un template',
196
- options: options.length ? options : [{ value: 'default', label: 'Default' }],
197
- initialValue: templateId,
198
- })
199
- if (p.isCancel(selected)) {
200
- p.cancel('Cancelado')
201
- process.exitCode = 1
202
- return
203
- }
204
- templateId = selected
205
- } else if (!templates.some((tpl) => tpl.id === templateId) && templateId !== 'default') {
206
- throw new Error(`Template desconocido: ${templateId}`)
207
- }
182
+ const templateId = CREATE_TEMPLATE_ID
208
183
 
209
184
  let pm = args.pm ?? detectPackageManager()
210
185
  if (!args.yes && !args.pm) {
@@ -228,7 +203,7 @@ export async function runCreate(argv) {
228
203
 
229
204
  const deployName = slugifyDeployName(folderName)
230
205
  const downloadSpinner = p.spinner()
231
- downloadSpinner.start(`Descargando template "${templateId}"…`)
206
+ downloadSpinner.start('Descargando template…')
232
207
  const zipBytes = await downloadTemplateZip(templateId)
233
208
  downloadSpinner.stop('Template descargado')
234
209
 
package/src/scaffold.mjs CHANGED
@@ -139,28 +139,85 @@ export function writeProjectConfig(destDir, { domain, deployName }) {
139
139
  }
140
140
 
141
141
  /**
142
+ * Dependencias nativas / postinstall del stack @ecomiq/storefront (Vite + workerd).
143
+ * pnpm 10+ bloquea sus scripts hasta aprobarlos (`ERR_PNPM_IGNORED_BUILDS`).
144
+ */
145
+ export const PNPM_ALLOW_BUILDS = Object.freeze(['esbuild', 'workerd'])
146
+
147
+ /**
148
+ * Escribe/actualiza `pnpm-workspace.yaml` para permitir builds de deps críticas.
149
+ * Compatible con pnpm 10 (`onlyBuiltDependencies`) y 11 (`allowBuilds`).
142
150
  * @param {string} destDir
143
- * @param {'npm' | 'pnpm' | 'yarn' | 'bun'} pm
151
+ * @param {readonly string[]} [packages]
144
152
  */
145
- export function installDependencies(destDir, pm) {
146
- const args =
147
- pm === 'yarn' ? ['install'] : pm === 'pnpm' ? ['install'] : pm === 'bun' ? ['install'] : ['install']
153
+ export function ensurePnpmAllowBuilds(destDir, packages = PNPM_ALLOW_BUILDS) {
154
+ const path = join(destDir, 'pnpm-workspace.yaml')
155
+ const existing = existsSync(path) ? readFileSync(path, 'utf8') : ''
156
+ const lines = []
157
+
158
+ if (existing.trim()) {
159
+ // Conserva contenido previo (packages:, etc.) y reescribe bloques de builds.
160
+ const withoutBuildBlocks = existing
161
+ .replace(/\n?allowBuilds:\n(?:[ \t]+.+\n)*/g, '\n')
162
+ .replace(/\n?onlyBuiltDependencies:\n(?:[ \t]+-.+\n)*/g, '\n')
163
+ .trimEnd()
164
+ if (withoutBuildBlocks.trim()) lines.push(withoutBuildBlocks, '')
165
+ }
148
166
 
167
+ lines.push('allowBuilds:')
168
+ for (const name of packages) {
169
+ lines.push(` ${name}: true`)
170
+ }
171
+ lines.push('')
172
+ lines.push('onlyBuiltDependencies:')
173
+ for (const name of packages) {
174
+ lines.push(` - ${name}`)
175
+ }
176
+ lines.push('')
177
+
178
+ writeFileSync(path, `${lines.join('\n')}\n`)
179
+ return path
180
+ }
181
+
182
+ /**
183
+ * @param {string} command
184
+ * @param {string[]} args
185
+ * @param {string} cwd
186
+ * @param {NodeJS.ProcessEnv} [env]
187
+ */
188
+ function runCommand(command, args, cwd, env = process.env) {
149
189
  return new Promise((resolvePromise, reject) => {
150
- const child = spawn(pm, args, {
151
- cwd: destDir,
190
+ const child = spawn(command, args, {
191
+ cwd,
152
192
  stdio: 'inherit',
153
193
  shell: process.platform === 'win32',
154
- env: { ...process.env, CI: process.env.CI ?? 'true' },
194
+ env,
155
195
  })
156
196
  child.on('exit', (code) => {
157
197
  if (code === 0) resolvePromise()
158
- else reject(new Error(`${pm} install salió con código ${code}`))
198
+ else reject(new Error(`${command} ${args.join(' ')} salió con código ${code}`))
159
199
  })
160
200
  child.on('error', reject)
161
201
  })
162
202
  }
163
203
 
204
+ /**
205
+ * @param {string} destDir
206
+ * @param {'npm' | 'pnpm' | 'yarn' | 'bun'} pm
207
+ */
208
+ export async function installDependencies(destDir, pm) {
209
+ // No forzar CI=true: en pnpm activa frozen-lockfile / strict builds y rompe scaffolds locales.
210
+ const env = { ...process.env }
211
+
212
+ if (pm === 'pnpm') {
213
+ ensurePnpmAllowBuilds(destDir)
214
+ await runCommand('pnpm', ['install'], destDir, env)
215
+ return
216
+ }
217
+
218
+ await runCommand(pm, ['install'], destDir, env)
219
+ }
220
+
164
221
  /**
165
222
  * Detecta package manager desde user-agent de npm_config o locks.
166
223
  * @param {string} [cwd]
package/src/templates.mjs CHANGED
@@ -6,9 +6,9 @@ export const DEFAULT_TEMPLATES_BASE_URL =
6
6
 
7
7
  export const FALLBACK_TEMPLATES = [
8
8
  {
9
- id: 'default',
10
- name: 'Default',
11
- description: 'Storefront base: catálogo, carrito, checkout y cuenta',
9
+ id: 'scaffold',
10
+ name: 'Scaffold',
11
+ description: 'Mínimo: home, catálogo, PDP, carrito, login y registro',
12
12
  },
13
13
  ]
14
14