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 +7 -9
- package/package.json +1 -1
- package/src/index.mjs +7 -32
- package/src/scaffold.mjs +65 -8
- package/src/templates.mjs +3 -3
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.
|
|
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
|
-
##
|
|
35
|
+
## Template
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
El zip se descarga desde R2:
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
- `https://pub-2b5396b516d940dbb1088d750701749c.r2.dev/template-{id}.zip`
|
|
39
|
+
`https://pub-2b5396b516d940dbb1088d750701749c.r2.dev/template-scaffold.zip`
|
|
42
40
|
|
|
43
|
-
Tras el scaffold
|
|
41
|
+
Tras el scaffold:
|
|
44
42
|
|
|
45
43
|
```bash
|
|
46
44
|
cd my-store
|
package/package.json
CHANGED
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
|
|
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 --
|
|
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
|
|
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(
|
|
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 {
|
|
151
|
+
* @param {readonly string[]} [packages]
|
|
144
152
|
*/
|
|
145
|
-
export function
|
|
146
|
-
const
|
|
147
|
-
|
|
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(
|
|
151
|
-
cwd
|
|
190
|
+
const child = spawn(command, args, {
|
|
191
|
+
cwd,
|
|
152
192
|
stdio: 'inherit',
|
|
153
193
|
shell: process.platform === 'win32',
|
|
154
|
-
env
|
|
194
|
+
env,
|
|
155
195
|
})
|
|
156
196
|
child.on('exit', (code) => {
|
|
157
197
|
if (code === 0) resolvePromise()
|
|
158
|
-
else reject(new Error(`${
|
|
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: '
|
|
10
|
-
name: '
|
|
11
|
-
description: '
|
|
9
|
+
id: 'scaffold',
|
|
10
|
+
name: 'Scaffold',
|
|
11
|
+
description: 'Mínimo: home, catálogo, PDP, carrito, login y registro',
|
|
12
12
|
},
|
|
13
13
|
]
|
|
14
14
|
|