create-snapify-app 1.1.0 → 1.1.3

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.
@@ -58,11 +58,18 @@ async function main() {
58
58
  }
59
59
  if (!nonInteractive) {
60
60
  console.log('\n PostgreSQL');
61
+ console.log(' (colle une URL Neon/Postgres, ou réponds o puis entre l’URL, ou n pour saisir hôte/port)');
61
62
  const paste = await askYesNo('Coller une DATABASE_URL complète', false);
62
- if (paste) {
63
- databaseUrl = await ask('DATABASE_URL');
63
+ if (paste && typeof paste === 'object' && paste.__databaseUrl) {
64
+ databaseUrl = paste.__databaseUrl;
65
+ ok('DATABASE_URL détectée');
66
+ } else if (paste) {
67
+ databaseUrl = await ask('DATABASE_URL (postgres://… ou postgresql://…)');
64
68
  }
65
69
  }
70
+ if (databaseUrl && !/^(postgres(ql)?:\/\/)/i.test(databaseUrl)) {
71
+ fail('DATABASE_URL invalide — doit commencer par postgres:// ou postgresql://');
72
+ }
66
73
  if (!databaseUrl) {
67
74
  const host = String(flags['db-host'] || (!nonInteractive ? await ask('Hôte', '127.0.0.1') : ''));
68
75
  const port = String(flags['db-port'] || (!nonInteractive ? await ask('Port', '5432') : '5432'));
@@ -119,22 +126,39 @@ async function main() {
119
126
  }
120
127
 
121
128
  if (!skipInstall) {
122
- console.log('\n→ npm install…');
123
- const install = spawnSync('npm', ['install', '--no-audit', '--no-fund', '--legacy-peer-deps'], {
129
+ // Windows : npm est npm.cmd — spawn sans shell échoue souvent sans message clair
130
+ const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
131
+ const npmOpts = {
124
132
  cwd: dest,
125
133
  stdio: 'inherit',
126
- env: { ...process.env, NODE_ENV: 'development' },
127
- });
128
- if (install.status !== 0) fail('npm install a échoué');
134
+ shell: process.platform === 'win32',
135
+ env: { ...process.env, NODE_ENV: 'development', npm_config_fund: 'false' },
136
+ };
137
+
138
+ console.log('\n→ npm install…');
139
+ const install = spawnSync(npmCmd, ['install', '--no-audit', '--no-fund', '--legacy-peer-deps'], npmOpts);
140
+ if (install.error) {
141
+ fail(`npm install : ${install.error.message}`);
142
+ }
143
+ if (install.status !== 0) {
144
+ fail(
145
+ `npm install a échoué (code ${install.status}).\n` +
146
+ ` Réessayez manuellement :\n` +
147
+ ` cd ${dest}\n` +
148
+ ` npm install --legacy-peer-deps`,
149
+ );
150
+ }
129
151
  ok('Dépendances installées');
130
152
 
131
153
  console.log('\n→ npm run build…');
132
- const build = spawnSync('npm', ['run', 'build'], {
133
- cwd: dest,
134
- stdio: 'inherit',
135
- env: { ...process.env, NODE_ENV: 'development' },
136
- });
137
- if (build.status !== 0) fail('npm run build a échoué');
154
+ const build = spawnSync(npmCmd, ['run', 'build'], npmOpts);
155
+ if (build.error) fail(`npm run build : ${build.error.message}`);
156
+ if (build.status !== 0) {
157
+ fail(
158
+ `npm run build a échoué (code ${build.status}).\n` +
159
+ ` Réessayez : cd ${dest} && npm run build`,
160
+ );
161
+ }
138
162
  ok('Frontend compilé');
139
163
  }
140
164
 
package/lib/prompt.mjs CHANGED
@@ -57,7 +57,12 @@ export async function askSecret(question) {
57
57
  /** @param {string} question @param {boolean} [fallback] */
58
58
  export async function askYesNo(question, fallback = true) {
59
59
  const def = fallback ? 'O/n' : 'o/N';
60
- const answer = (await ask(`${question} [${def}]`, '')).toLowerCase();
60
+ const answer = (await ask(`${question} [${def}]`, '')).trim();
61
61
  if (!answer) return fallback;
62
- return answer === 'o' || answer === 'oui' || answer === 'y' || answer === 'yes';
62
+ const lower = answer.toLowerCase();
63
+ // Si l'utilisateur colle directement une URL postgres au lieu de o/N
64
+ if (/^(postgres(ql)?:\/\/)/i.test(answer)) {
65
+ return { __databaseUrl: answer };
66
+ }
67
+ return lower === 'o' || lower === 'oui' || lower === 'y' || lower === 'yes';
63
68
  }
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "create-snapify-app",
3
- "version": "1.1.0",
3
+ "version": "1.1.3",
4
4
  "description": "Installe Snapify (CMS complet : backend + frontend) : npx create-snapify-app@latest",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "author": "NOVALABS WEB DESIGN <contact@novalabs.tn> (https://novalabs.tn)",
7
7
  "homepage": "https://snapify.tn",
8
- "bugs": { "email": "contact@novalabs.tn" },
8
+ "bugs": {
9
+ "email": "contact@novalabs.tn"
10
+ },
9
11
  "keywords": [
10
12
  "snapify",
11
13
  "cms",
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "package": "create-snapify-app",
3
- "version": "1.1.0",
4
- "packedAt": "2026-11-07T14:01:09.199Z"
3
+ "version": "1.1.3",
4
+ "packedAt": "2026-11-07T14:13:11.077Z"
5
5
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "snapify",
3
3
  "private": true,
4
- "version": "1.1.0",
4
+ "version": "1.1.3",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "NOVALABS WEB DESIGN <contact@novalabs.tn>",
7
7
  "type": "module",
@@ -87,10 +87,56 @@ async function main() {
87
87
  await fs.rm(npmEnv.NPM_CONFIG_USERCONFIG, { force: true }).catch(() => undefined);
88
88
  }
89
89
  }
90
+ if (dryRun) {
91
+ console.log('✔ Contrôle terminé : rien n’a été publié');
92
+ return;
93
+ }
94
+
95
+ // Vérifie version + tarball téléchargeable (évite métadonnées sans .tgz / 0.0.0-stage)
96
+ let confirmed = '';
97
+ let tarballOk = false;
98
+ for (let i = 0; i < 12; i++) {
99
+ await new Promise((r) => setTimeout(r, i === 0 ? 2000 : 3000));
100
+ try {
101
+ confirmed = execFileSync('npm', ['view', `${pkg.name}@${pkg.version}`, 'version'], {
102
+ cwd: ROOT,
103
+ encoding: 'utf8',
104
+ env: npmEnv,
105
+ stdio: ['ignore', 'pipe', 'ignore'],
106
+ }).trim();
107
+ } catch {
108
+ confirmed = '';
109
+ }
110
+ try {
111
+ const tarball = execFileSync('npm', ['view', `${pkg.name}@${pkg.version}`, 'dist.tarball'], {
112
+ cwd: ROOT,
113
+ encoding: 'utf8',
114
+ env: npmEnv,
115
+ stdio: ['ignore', 'pipe', 'ignore'],
116
+ }).trim();
117
+ if (tarball.startsWith('http')) {
118
+ const head = execFileSync('curl', ['-sS', '-o', '/dev/null', '-w', '%{http_code}', '-L', tarball], {
119
+ encoding: 'utf8',
120
+ }).trim();
121
+ tarballOk = head === '200';
122
+ }
123
+ } catch {
124
+ tarballOk = false;
125
+ }
126
+ if (confirmed === pkg.version && tarballOk) break;
127
+ console.log(
128
+ `• Attente npm… (${i + 1}/12) version=${confirmed || '—'} tarball=${tarballOk ? 'ok' : '404'}`,
129
+ );
130
+ }
131
+ if (confirmed !== pkg.version || !tarballOk) {
132
+ throw new Error(
133
+ `Publication incomplète : ${pkg.name}@${pkg.version} version=${confirmed || 'absente'} tarball=${tarballOk ? 'ok' : '404'}. ` +
134
+ `Souvent un publish « staged » sans blob. Bump la version et republie avec --otp.`,
135
+ );
136
+ }
90
137
  console.log(
91
- dryRun
92
- ? '✔ Contrôle terminé : rien n’a été publié'
93
- : `✔ Publié : https://www.npmjs.com/package/${pkg.name}\n Install : npx create-snapify-app@latest`,
138
+ `✔ Publié et confirmé (tarball 200) : https://www.npmjs.com/package/${pkg.name}/v/${pkg.version}\n` +
139
+ ` Install : npx create-snapify-app@${pkg.version}`,
94
140
  );
95
141
  }
96
142