@lystech/core 1.0.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 +2 -0
- package/bin/README.md +126 -0
- package/bin/attach.cjs +277 -0
- package/bin/check-pwa.cjs +277 -0
- package/bin/setup-github-action.cjs +209 -0
- package/dist/index.d.ts +978 -0
- package/dist/lystech-core-provider.es.js +48163 -0
- package/dist/lystech-core-provider.umd.js +1719 -0
- package/dist/lystech-core-styles.css +1 -0
- package/package.json +73 -0
package/README.md
ADDED
package/bin/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# 📦 Scripts LystechCoreNpmPackage
|
|
2
|
+
|
|
3
|
+
## Commandes Disponibles
|
|
4
|
+
|
|
5
|
+
### 🔗 `npx lystechcorenpmpackage attach`
|
|
6
|
+
|
|
7
|
+
**Attache votre projet au système de mise à jour automatique.**
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx lystechcorenpmpackage attach
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Ce que ça fait:**
|
|
14
|
+
|
|
15
|
+
- Détecte automatiquement votre repo GitHub
|
|
16
|
+
- Génère la configuration pour `consumers.json`
|
|
17
|
+
- Guide pour compléter le setup (PR, secrets, etc.)
|
|
18
|
+
|
|
19
|
+
**Fichier**: `bin/attach.cjs`
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
### ⚙️ `npx lystechcorenpmpackage lystech-auto-update-init` (Legacy)
|
|
24
|
+
|
|
25
|
+
**Crée un workflow optionnel de fallback dans votre projet.**
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx lystechcorenpmpackage lystech-auto-update-init [--force]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Options:**
|
|
32
|
+
|
|
33
|
+
- `--force`: Écrase le workflow existant
|
|
34
|
+
- `--dry-run`: Affiche le YAML sans créer de fichier
|
|
35
|
+
- `--package=<name>`: Nom du package à surveiller
|
|
36
|
+
- `--deploy-script=<script>`: Script de déploiement custom
|
|
37
|
+
|
|
38
|
+
**Note**: Ce workflow n'est plus nécessaire avec le nouveau système centralisé, mais peut servir de fallback manuel.
|
|
39
|
+
|
|
40
|
+
**Fichier**: `bin/setup-github-action.cjs`
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
### 🔍 `npx lystechcorenpmpackage check-pwa`
|
|
45
|
+
|
|
46
|
+
**Vérifie la configuration PWA de votre projet.**
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx lystechcorenpmpackage check-pwa
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Fichier**: `bin/check-pwa.cjs`
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 🚀 Workflow Recommandé pour Nouveaux Projets
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# 1. Installer le package
|
|
60
|
+
npm install lystechcorenpmpackage
|
|
61
|
+
|
|
62
|
+
# 2. Attacher au système de mise à jour
|
|
63
|
+
npx lystechcorenpmpackage attach
|
|
64
|
+
|
|
65
|
+
# 3. Suivre les instructions affichées
|
|
66
|
+
|
|
67
|
+
# ✅ Terminé!
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 🔧 Pour les Développeurs du Package
|
|
73
|
+
|
|
74
|
+
### Structure
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
bin/
|
|
78
|
+
├── attach.cjs # Commande principale d'attachement
|
|
79
|
+
├── setup-github-action.cjs # (Legacy) Génération de workflow local
|
|
80
|
+
├── check-pwa.cjs # Vérification PWA
|
|
81
|
+
├── notify-consumers.cjs # (Legacy) Notification manuelle
|
|
82
|
+
└── README.md # Ce fichier
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Ajouter une Nouvelle Commande
|
|
86
|
+
|
|
87
|
+
1. Créer le fichier dans `bin/`
|
|
88
|
+
2. Ajouter le shebang: `#!/usr/bin/env node`
|
|
89
|
+
3. Rendre exécutable: `chmod +x bin/nouvelle-commande.cjs`
|
|
90
|
+
4. Ajouter dans `package.json` → `bin`:
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"bin": {
|
|
94
|
+
"nouvelle-commande": "./bin/nouvelle-commande.cjs"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Tester Localement
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Depuis le package
|
|
103
|
+
node bin/attach.cjs
|
|
104
|
+
|
|
105
|
+
# Ou depuis n'importe où
|
|
106
|
+
npm link
|
|
107
|
+
npx lystechcorenpmpackage attach
|
|
108
|
+
npm unlink
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 📚 Documentation
|
|
114
|
+
|
|
115
|
+
- [Guide d'attachement](../ATTACH-GUIDE.md) - Guide utilisateur pour `attach`
|
|
116
|
+
- [Guide Auto-Update complet](../AUTO-UPDATE-GUIDE.md) - Documentation complète du système
|
|
117
|
+
- [consumers.json](../consumers.json) - Configuration des projets
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 🆘 Support
|
|
122
|
+
|
|
123
|
+
Pour toute question ou problème:
|
|
124
|
+
|
|
125
|
+
- 📖 Lire la documentation ci-dessus
|
|
126
|
+
- 🐛 [Créer une issue](https://github.com/eracine4/lystechcorenpmpackage/issues)
|
package/bin/attach.cjs
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Script pour attacher un projet au système de mise à jour automatique
|
|
5
|
+
*
|
|
6
|
+
* Usage: npx lystechcorenpmpackage attach
|
|
7
|
+
*
|
|
8
|
+
* Ce script:
|
|
9
|
+
* 1. Détecte le repo GitHub actuel
|
|
10
|
+
* 2. Génère la configuration pour consumers.json
|
|
11
|
+
* 3. Guide l'utilisateur pour compléter le setup
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require("fs");
|
|
15
|
+
const path = require("path");
|
|
16
|
+
const { execSync } = require("child_process");
|
|
17
|
+
|
|
18
|
+
// Couleurs console
|
|
19
|
+
const colors = {
|
|
20
|
+
reset: "\x1b[0m",
|
|
21
|
+
bright: "\x1b[1m",
|
|
22
|
+
green: "\x1b[32m",
|
|
23
|
+
yellow: "\x1b[33m",
|
|
24
|
+
blue: "\x1b[34m",
|
|
25
|
+
cyan: "\x1b[36m",
|
|
26
|
+
red: "\x1b[31m",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function log(message, color = colors.reset) {
|
|
30
|
+
console.log(`${color}${message}${colors.reset}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function header(message) {
|
|
34
|
+
console.log("");
|
|
35
|
+
log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`, colors.cyan);
|
|
36
|
+
log(` ${message}`, colors.bright + colors.cyan);
|
|
37
|
+
log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`, colors.cyan);
|
|
38
|
+
console.log("");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function success(message) {
|
|
42
|
+
log(`✅ ${message}`, colors.green);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function warning(message) {
|
|
46
|
+
log(`⚠️ ${message}`, colors.yellow);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function error(message) {
|
|
50
|
+
log(`❌ ${message}`, colors.red);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function info(message) {
|
|
54
|
+
log(`ℹ️ ${message}`, colors.blue);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Détecte les informations Git du repo actuel
|
|
59
|
+
*/
|
|
60
|
+
function detectGitInfo() {
|
|
61
|
+
try {
|
|
62
|
+
// Vérifier si on est dans un repo git
|
|
63
|
+
execSync("git rev-parse --git-dir", { stdio: "ignore" });
|
|
64
|
+
|
|
65
|
+
// Récupérer l'URL remote
|
|
66
|
+
const remoteUrl = execSync("git config --get remote.origin.url", {
|
|
67
|
+
encoding: "utf8",
|
|
68
|
+
}).trim();
|
|
69
|
+
|
|
70
|
+
// Parser l'URL GitHub
|
|
71
|
+
const match = remoteUrl.match(/github\.com[:/]([^/]+)\/([^/\.]+)/);
|
|
72
|
+
if (!match) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const [, owner, repo] = match;
|
|
77
|
+
|
|
78
|
+
// Récupérer la branche par défaut
|
|
79
|
+
let defaultBranch = "master";
|
|
80
|
+
try {
|
|
81
|
+
defaultBranch = execSync(
|
|
82
|
+
"git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'",
|
|
83
|
+
{ encoding: "utf8", shell: "bash" },
|
|
84
|
+
).trim();
|
|
85
|
+
} catch {
|
|
86
|
+
// Essayer une autre méthode
|
|
87
|
+
try {
|
|
88
|
+
defaultBranch =
|
|
89
|
+
execSync("git branch -r", { encoding: "utf8" })
|
|
90
|
+
.split("\n")
|
|
91
|
+
.find((b) => b.includes("HEAD"))
|
|
92
|
+
?.split("->")[1]
|
|
93
|
+
?.trim()
|
|
94
|
+
?.replace("origin/", "") || "master";
|
|
95
|
+
} catch {
|
|
96
|
+
// Garder master par défaut
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { owner, repo, branch: defaultBranch };
|
|
101
|
+
} catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Lit le package.json du projet
|
|
108
|
+
*/
|
|
109
|
+
function readPackageJson() {
|
|
110
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
111
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
return JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Génère la configuration pour consumers.json
|
|
119
|
+
*/
|
|
120
|
+
function generateConsumerConfig(gitInfo, packageJson) {
|
|
121
|
+
const scripts = packageJson?.scripts || {};
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
owner: gitInfo.owner,
|
|
125
|
+
repo: gitInfo.repo,
|
|
126
|
+
branch: gitInfo.branch,
|
|
127
|
+
enabled: true,
|
|
128
|
+
buildScript: scripts.build ? "build" : "",
|
|
129
|
+
deployScript: scripts.deploy ? "deploy" : "",
|
|
130
|
+
deployTarget: scripts.deploy?.includes("firebase")
|
|
131
|
+
? "firebase"
|
|
132
|
+
: scripts.deploy?.includes("vercel")
|
|
133
|
+
? "vercel"
|
|
134
|
+
: "custom",
|
|
135
|
+
notes: packageJson?.description || `Projet ${gitInfo.repo}`,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Affiche les instructions pour compléter le setup
|
|
141
|
+
*/
|
|
142
|
+
function printInstructions(config) {
|
|
143
|
+
header("📋 Configuration générée");
|
|
144
|
+
|
|
145
|
+
log(
|
|
146
|
+
"Ajoutez cette entrée au fichier consumers.json du package:",
|
|
147
|
+
colors.bright,
|
|
148
|
+
);
|
|
149
|
+
console.log("");
|
|
150
|
+
console.log(JSON.stringify(config, null, 2));
|
|
151
|
+
console.log("");
|
|
152
|
+
|
|
153
|
+
header("🚀 Prochaines étapes");
|
|
154
|
+
|
|
155
|
+
log("Pour activer la mise à jour automatique:", colors.bright);
|
|
156
|
+
console.log("");
|
|
157
|
+
|
|
158
|
+
log(
|
|
159
|
+
"1️⃣ Créer une Pull Request sur le repo lystechcorenpmpackage:",
|
|
160
|
+
colors.cyan,
|
|
161
|
+
);
|
|
162
|
+
console.log(" - Ouvrir: https://github.com/eracine4/lystechcorenpmpackage");
|
|
163
|
+
console.log(" - Éditer: consumers.json");
|
|
164
|
+
console.log(" - Ajouter la configuration ci-dessus");
|
|
165
|
+
console.log("");
|
|
166
|
+
|
|
167
|
+
log("2️⃣ Configurer le token GitHub (dans le repo du package):", colors.cyan);
|
|
168
|
+
console.log(" Si ce n'est pas déjà fait:");
|
|
169
|
+
console.log(" a) Créer un Personal Access Token:");
|
|
170
|
+
console.log(
|
|
171
|
+
" https://github.com/settings/tokens/new?scopes=repo&description=Lystech%20Auto%20Update",
|
|
172
|
+
);
|
|
173
|
+
console.log(" b) Ajouter le secret CONSUMER_UPDATE_TOKEN:");
|
|
174
|
+
console.log(
|
|
175
|
+
" https://github.com/eracine4/lystechcorenpmpackage/settings/secrets/actions",
|
|
176
|
+
);
|
|
177
|
+
console.log("");
|
|
178
|
+
|
|
179
|
+
if (config.deployScript) {
|
|
180
|
+
log(
|
|
181
|
+
`3️⃣ Configurer les secrets de déploiement (${config.deployTarget}):`,
|
|
182
|
+
colors.cyan,
|
|
183
|
+
);
|
|
184
|
+
console.log(
|
|
185
|
+
` Dans votre repo: https://github.com/${config.owner}/${config.repo}/settings/secrets/actions`,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
if (config.deployTarget === "firebase") {
|
|
189
|
+
console.log(" - FIREBASE_TOKEN (requis pour npm run deploy)");
|
|
190
|
+
} else if (config.deployTarget === "vercel") {
|
|
191
|
+
console.log(" - VERCEL_TOKEN");
|
|
192
|
+
}
|
|
193
|
+
console.log("");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
log(
|
|
197
|
+
"✅ Une fois la PR mergée, vos updates seront automatiques!",
|
|
198
|
+
colors.green,
|
|
199
|
+
);
|
|
200
|
+
console.log("");
|
|
201
|
+
info(
|
|
202
|
+
"Le package mettra à jour ce projet automatiquement à chaque publication.",
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Crée un fichier de référence local
|
|
208
|
+
*/
|
|
209
|
+
function createLocalReference(config) {
|
|
210
|
+
const refPath = path.join(process.cwd(), ".lystech-auto-update.json");
|
|
211
|
+
fs.writeFileSync(refPath, JSON.stringify(config, null, 2));
|
|
212
|
+
success(`Configuration sauvegardée dans: .lystech-auto-update.json`);
|
|
213
|
+
warning(
|
|
214
|
+
"Ajoutez ce fichier au .gitignore si vous ne voulez pas le committer",
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Main
|
|
220
|
+
*/
|
|
221
|
+
function main() {
|
|
222
|
+
header("🔗 Lystech Auto-Update - Attachement");
|
|
223
|
+
|
|
224
|
+
// Détecter le repo
|
|
225
|
+
const gitInfo = detectGitInfo();
|
|
226
|
+
if (!gitInfo) {
|
|
227
|
+
error("Impossible de détecter les informations Git");
|
|
228
|
+
console.log("Assurez-vous d'être dans un repo GitHub cloné");
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
success(
|
|
233
|
+
`Repo détecté: ${gitInfo.owner}/${gitInfo.repo} (branche: ${gitInfo.branch})`,
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
// Lire package.json
|
|
237
|
+
const packageJson = readPackageJson();
|
|
238
|
+
if (!packageJson) {
|
|
239
|
+
error("package.json introuvable");
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
success(`Projet: ${packageJson.name || gitInfo.repo}`);
|
|
244
|
+
|
|
245
|
+
// Vérifier si le package est installé
|
|
246
|
+
const hasPackage =
|
|
247
|
+
packageJson.dependencies?.lystechcorenpmpackage ||
|
|
248
|
+
packageJson.devDependencies?.lystechcorenpmpackage;
|
|
249
|
+
|
|
250
|
+
if (!hasPackage) {
|
|
251
|
+
warning("lystechcorenpmpackage n'est pas dans les dépendances");
|
|
252
|
+
console.log("Installez-le d'abord: npm install lystechcorenpmpackage");
|
|
253
|
+
console.log("");
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Générer la config
|
|
257
|
+
const config = generateConsumerConfig(gitInfo, packageJson);
|
|
258
|
+
|
|
259
|
+
// Sauvegarder localement
|
|
260
|
+
createLocalReference(config);
|
|
261
|
+
|
|
262
|
+
// Afficher les instructions
|
|
263
|
+
printInstructions(config);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Point d'entrée
|
|
267
|
+
if (require.main === module) {
|
|
268
|
+
try {
|
|
269
|
+
main();
|
|
270
|
+
} catch (err) {
|
|
271
|
+
error(`Erreur: ${err.message}`);
|
|
272
|
+
console.error(err);
|
|
273
|
+
process.exit(1);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
module.exports = { detectGitInfo, generateConsumerConfig };
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Script universel de vérification PWA pour un projet Node.js (CommonJS)
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
|
|
7
|
+
function findFileUpwards(filename, startDir = process.cwd()) {
|
|
8
|
+
let dir = startDir;
|
|
9
|
+
while (dir !== path.parse(dir).root) {
|
|
10
|
+
const candidate = path.join(dir, filename);
|
|
11
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
12
|
+
dir = path.dirname(dir);
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function checkManifest(autoCreate = false) {
|
|
18
|
+
const manifestPaths = [
|
|
19
|
+
findFileUpwards("public/manifest.json"),
|
|
20
|
+
findFileUpwards("manifest.json"),
|
|
21
|
+
].filter(Boolean);
|
|
22
|
+
const defaultManifest = {
|
|
23
|
+
name: "Lystech Core",
|
|
24
|
+
short_name: "Lystech",
|
|
25
|
+
start_url: ".",
|
|
26
|
+
display: "standalone",
|
|
27
|
+
background_color: "#ffffff",
|
|
28
|
+
theme_color: "#000000",
|
|
29
|
+
icons: [
|
|
30
|
+
{
|
|
31
|
+
src: "/logo.png",
|
|
32
|
+
sizes: "192x192",
|
|
33
|
+
type: "image/png",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
src: "/logo.png",
|
|
37
|
+
sizes: "512x512",
|
|
38
|
+
type: "image/png",
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
let manifestPath = manifestPaths[0];
|
|
43
|
+
if (!manifestPath && autoCreate) {
|
|
44
|
+
const publicDir = findFileUpwards("public") || process.cwd();
|
|
45
|
+
manifestPath = fs.existsSync(path.join(publicDir, "public"))
|
|
46
|
+
? path.join(publicDir, "public", "manifest.json")
|
|
47
|
+
: path.join(publicDir, "manifest.json");
|
|
48
|
+
fs.writeFileSync(manifestPath, JSON.stringify(defaultManifest, null, 2));
|
|
49
|
+
console.log("🟢 manifest.json créé :", manifestPath);
|
|
50
|
+
return manifestPath;
|
|
51
|
+
}
|
|
52
|
+
if (manifestPath) {
|
|
53
|
+
let content;
|
|
54
|
+
try {
|
|
55
|
+
content = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
56
|
+
} catch (e) {
|
|
57
|
+
if (autoCreate) {
|
|
58
|
+
fs.writeFileSync(
|
|
59
|
+
manifestPath,
|
|
60
|
+
JSON.stringify(defaultManifest, null, 2)
|
|
61
|
+
);
|
|
62
|
+
console.log("🟢 manifest.json réparé :", manifestPath);
|
|
63
|
+
return manifestPath;
|
|
64
|
+
} else {
|
|
65
|
+
console.error("❌ manifest.json illisible :", manifestPath);
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
let changed = false;
|
|
70
|
+
for (const key of Object.keys(defaultManifest)) {
|
|
71
|
+
if (!(key in content)) {
|
|
72
|
+
content[key] = defaultManifest[key];
|
|
73
|
+
changed = true;
|
|
74
|
+
console.log(`🟢 Champ ajouté à manifest.json : ${key}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (changed && autoCreate) {
|
|
78
|
+
fs.writeFileSync(manifestPath, JSON.stringify(content, null, 2));
|
|
79
|
+
console.log("🟢 manifest.json mis à jour :", manifestPath);
|
|
80
|
+
} else {
|
|
81
|
+
console.log("✅ manifest.json trouvé :", manifestPath);
|
|
82
|
+
}
|
|
83
|
+
return manifestPath;
|
|
84
|
+
}
|
|
85
|
+
console.error(
|
|
86
|
+
"❌ Aucun manifest.json trouvé (ni dans public/, ni à la racine)"
|
|
87
|
+
);
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function checkServiceWorker(autoCreate = false) {
|
|
92
|
+
const swPaths = [
|
|
93
|
+
findFileUpwards("public/service-worker.js"),
|
|
94
|
+
findFileUpwards("service-worker.js"),
|
|
95
|
+
].filter(Boolean);
|
|
96
|
+
const requiredFunctions = {
|
|
97
|
+
install: `self.addEventListener("install", (event) => {
|
|
98
|
+
self.skipWaiting();
|
|
99
|
+
});`,
|
|
100
|
+
activate: `self.addEventListener("activate", (event) => {
|
|
101
|
+
event.waitUntil(self.clients.claim());
|
|
102
|
+
});`,
|
|
103
|
+
push: `self.addEventListener("push", (event) => {
|
|
104
|
+
const data = event.data ? event.data.json() : {};
|
|
105
|
+
const title = data.title || "Nouvelle notification";
|
|
106
|
+
const options = {
|
|
107
|
+
body: data.body || "Vous avez une nouvelle notification.",
|
|
108
|
+
icon: data.icon || "https://placehold.co/192x192/000000/FFFFFF?text=PUSH",
|
|
109
|
+
badge: data.badge || "https://placehold.co/72x72/000000/FFFFFF?text=BADGE",
|
|
110
|
+
image: data.image,
|
|
111
|
+
data: {
|
|
112
|
+
url: data.data?.url || "/",
|
|
113
|
+
},
|
|
114
|
+
actions: data.actions || [],
|
|
115
|
+
vibrate: [200, 100, 200],
|
|
116
|
+
};
|
|
117
|
+
event.waitUntil(self.registration.showNotification(title, options));
|
|
118
|
+
});`,
|
|
119
|
+
notificationclick: `self.addEventListener("notificationclick", (event) => {
|
|
120
|
+
event.notification.close();
|
|
121
|
+
const clickedNotification = event.notification;
|
|
122
|
+
const urlToOpen = clickedNotification.data.url;
|
|
123
|
+
event.waitUntil(
|
|
124
|
+
self.clients.matchAll({ type: "window" }).then((clientList) => {
|
|
125
|
+
for (const client of clientList) {
|
|
126
|
+
if (client.url === urlToOpen && "focus" in client) {
|
|
127
|
+
return client.focus();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (self.clients.openWindow) {
|
|
131
|
+
return self.clients.openWindow(urlToOpen);
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
);
|
|
135
|
+
});`,
|
|
136
|
+
};
|
|
137
|
+
let swPath = swPaths[0];
|
|
138
|
+
if (!swPath && autoCreate) {
|
|
139
|
+
const publicDir = findFileUpwards("public") || process.cwd();
|
|
140
|
+
swPath = fs.existsSync(path.join(publicDir, "public"))
|
|
141
|
+
? path.join(publicDir, "public", "service-worker.js")
|
|
142
|
+
: path.join(publicDir, "service-worker.js");
|
|
143
|
+
const swContent = Object.values(requiredFunctions).join("\n\n");
|
|
144
|
+
fs.writeFileSync(swPath, swContent);
|
|
145
|
+
console.log("🟢 service-worker.js créé :", swPath);
|
|
146
|
+
return swPath;
|
|
147
|
+
}
|
|
148
|
+
if (swPath) {
|
|
149
|
+
let content = fs.readFileSync(swPath, "utf8");
|
|
150
|
+
let changed = false;
|
|
151
|
+
function escapeRegExp(str) {
|
|
152
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
153
|
+
}
|
|
154
|
+
for (const fn of Object.values(requiredFunctions)) {
|
|
155
|
+
const signature = fn.split("\n")[0].trim();
|
|
156
|
+
// On veut matcher jusqu'à la fermeture de l'accolade et du parenthèse, peu importe les espaces
|
|
157
|
+
const regex = new RegExp(
|
|
158
|
+
escapeRegExp(signature) + "[\\s\\S]*?\\}s*\\);",
|
|
159
|
+
"gm"
|
|
160
|
+
);
|
|
161
|
+
if (regex.test(content)) {
|
|
162
|
+
// Remplacer la fonction existante par la version canonique
|
|
163
|
+
content = content.replace(regex, fn);
|
|
164
|
+
changed = true;
|
|
165
|
+
console.log(`🟢 Fonction remplacée : ${signature}`);
|
|
166
|
+
} else {
|
|
167
|
+
// Ajouter la fonction manquante
|
|
168
|
+
content += "\n\n" + fn;
|
|
169
|
+
changed = true;
|
|
170
|
+
console.log(`🟢 Fonction ajoutée : ${signature}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (changed && autoCreate) {
|
|
174
|
+
fs.writeFileSync(swPath, content.trim() + "\n");
|
|
175
|
+
console.log("🟢 service-worker.js mis à jour :", swPath);
|
|
176
|
+
} else {
|
|
177
|
+
console.log("✅ service-worker.js trouvé :", swPath);
|
|
178
|
+
}
|
|
179
|
+
return swPath;
|
|
180
|
+
}
|
|
181
|
+
console.error(
|
|
182
|
+
"❌ Aucun service-worker.js trouvé (ni dans public/, ni à la racine)"
|
|
183
|
+
);
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function checkIndexHtmlForManifestAndAppleTags(autoCreate = false) {
|
|
188
|
+
const indexPaths = [
|
|
189
|
+
findFileUpwards("public/index.html"),
|
|
190
|
+
findFileUpwards("index.html"),
|
|
191
|
+
].filter(Boolean);
|
|
192
|
+
if (indexPaths.length === 0) {
|
|
193
|
+
console.error(
|
|
194
|
+
"❌ Aucun index.html trouvé (ni dans public/, ni à la racine)"
|
|
195
|
+
);
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
const indexPath = indexPaths[0];
|
|
199
|
+
let lines = fs.readFileSync(indexPath, "utf8").split(/\r?\n/);
|
|
200
|
+
let ok = true;
|
|
201
|
+
const requiredLines = [
|
|
202
|
+
'<link rel="manifest" href="/manifest.json" />',
|
|
203
|
+
'<meta name="apple-mobile-web-app-capable" content="yes" />',
|
|
204
|
+
'<meta name="apple-mobile-web-app-status-bar-style" content="default" />',
|
|
205
|
+
'<meta name="apple-mobile-web-app-title" content="Lystech Core" />',
|
|
206
|
+
'<link rel="apple-touch-icon" href="/logo.png" />',
|
|
207
|
+
];
|
|
208
|
+
let changed = false;
|
|
209
|
+
for (const tag of requiredLines) {
|
|
210
|
+
if (lines.some((l) => l.includes(tag.replace(/\s*\/?>$/, "")))) {
|
|
211
|
+
console.log(`✅ ${tag} trouvé dans`, indexPath);
|
|
212
|
+
} else {
|
|
213
|
+
ok = false;
|
|
214
|
+
if (autoCreate) {
|
|
215
|
+
// Ajoute la ligne avant </head>
|
|
216
|
+
const headIdx = lines.findIndex((l) => l.match(/<\/head>/i));
|
|
217
|
+
if (headIdx !== -1) {
|
|
218
|
+
lines.splice(headIdx, 0, tag);
|
|
219
|
+
changed = true;
|
|
220
|
+
console.log(`🟢 ${tag} ajouté dans`, indexPath);
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
console.error(`❌ ${tag} manquant dans`, indexPath);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (changed && autoCreate) {
|
|
228
|
+
fs.writeFileSync(indexPath, lines.join("\n"));
|
|
229
|
+
ok = true;
|
|
230
|
+
}
|
|
231
|
+
return ok;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function main() {
|
|
235
|
+
console.log("--- Vérification Progressive Web App ---");
|
|
236
|
+
const autoCreate =
|
|
237
|
+
process.argv.includes("--fix") || process.argv.includes("--create");
|
|
238
|
+
const manifest = checkManifest(autoCreate);
|
|
239
|
+
const serviceWorker = checkServiceWorker(autoCreate);
|
|
240
|
+
const indexHasManifestAndApple =
|
|
241
|
+
checkIndexHtmlForManifestAndAppleTags(autoCreate);
|
|
242
|
+
|
|
243
|
+
// Ajout automatique du script check-pwa dans package.json si absent
|
|
244
|
+
const pkgPath = findFileUpwards("package.json");
|
|
245
|
+
if (pkgPath) {
|
|
246
|
+
try {
|
|
247
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
248
|
+
if (!pkg.scripts) pkg.scripts = {};
|
|
249
|
+
if (!pkg.scripts["check-pwa"]) {
|
|
250
|
+
pkg.scripts["check-pwa"] = "npx lystechcorenpmpackage check-pwa --fix";
|
|
251
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
|
|
252
|
+
console.log("🟢 Script 'check-pwa' ajouté dans package.json");
|
|
253
|
+
} else {
|
|
254
|
+
console.log("✅ Script 'check-pwa' déjà présent dans package.json");
|
|
255
|
+
}
|
|
256
|
+
} catch (e) {
|
|
257
|
+
console.error(
|
|
258
|
+
"❌ Erreur lors de la mise à jour du package.json :",
|
|
259
|
+
e.message
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (manifest && serviceWorker && indexHasManifestAndApple) {
|
|
264
|
+
console.log("🎉 Votre projet est prêt pour la Progressive Web App!");
|
|
265
|
+
process.exit(0);
|
|
266
|
+
} else {
|
|
267
|
+
if (!manifest && autoCreate)
|
|
268
|
+
console.error("Impossible de créer/mettre à jour manifest.json");
|
|
269
|
+
if (!serviceWorker && autoCreate)
|
|
270
|
+
console.error("Impossible de créer/mettre à jour service-worker.js");
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (require.main === module) {
|
|
276
|
+
main();
|
|
277
|
+
}
|