@lystech/core 1.0.3 → 3.0.4
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 +1 -1
- package/bin/README.md +126 -126
- package/bin/attach.cjs +277 -277
- package/bin/check-pwa.cjs +276 -276
- package/bin/setup-github-action.cjs +209 -209
- package/dist/lystech-core-provider.es.js +1 -1
- package/dist/lystech-core-provider.umd.js +1 -1
- package/package.json +73 -73
package/bin/check-pwa.cjs
CHANGED
|
@@ -1,277 +1,277 @@
|
|
|
1
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
|
-
}
|
|
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
|
+
}
|