odoro 0.1.2 → 0.1.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/dist/{build-SNTGJH2J.js → build-J2J3NBV5.js} +1 -1
- package/dist/{chunk-PEMYUK2D.js → chunk-3SZIN6VG.js} +2 -2
- package/dist/chunk-JGXJBVI7.js +391 -0
- package/dist/{chunk-T42X2NJN.js → chunk-Q35CGY2Z.js} +5 -1
- package/dist/cli.js +6 -6
- package/dist/{commands-V44Y5G4F.js → commands-4JRBD55Z.js} +3 -3
- package/dist/{create-Z3TSGBE6.js → create-XJM3TUAQ.js} +1 -1
- package/dist/index.d.ts +25 -0
- package/dist/index.js +2 -2
- package/dist/{package-E26GKHFT.js → package-VRJRDQY3.js} +10 -1
- package/package.json +10 -1
- package/templates/react-ts/README.md +5 -6
- package/dist/chunk-UGRSODHU.js +0 -154
|
@@ -52,9 +52,9 @@ async function assertEnvIgnored(target) {
|
|
|
52
52
|
return ignored ? void 0 : ".env ne figure pas dans le .gitignore : vos identifiants risquent d etre versionnes.";
|
|
53
53
|
}
|
|
54
54
|
var PROVIDER_PENDING = [
|
|
55
|
-
"Le provisionnement passe par @odoro/cloud-sdk, installe a part :",
|
|
55
|
+
"Le provisionnement passe par @odoro-cli/cloud-sdk, installe a part :",
|
|
56
56
|
"",
|
|
57
|
-
" npm install --save-dev @odoro/cloud-sdk",
|
|
57
|
+
" npm install --save-dev @odoro-cli/cloud-sdk",
|
|
58
58
|
" odoro db:login",
|
|
59
59
|
" odoro db:create --env production",
|
|
60
60
|
"",
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { extractEntries, applyAlias, isBareSpecifier } from './chunk-G5QXVBYT.js';
|
|
3
|
+
import { info, colors, size, success, duration } from './chunk-JMEHF3KN.js';
|
|
4
|
+
import { existsSync } from 'fs';
|
|
5
|
+
import { readFile, rm, mkdir, writeFile, cp, stat } from 'fs/promises';
|
|
6
|
+
import { join, basename, resolve, relative, sep } from 'path';
|
|
7
|
+
import { build } from 'esbuild';
|
|
8
|
+
import { createRequire } from 'module';
|
|
9
|
+
import { pathToFileURL } from 'url';
|
|
10
|
+
|
|
11
|
+
// src/build/elaguer.ts
|
|
12
|
+
var GROUPES = /* @__PURE__ */ new Set(["media", "supports", "layer", "container", "scope"]);
|
|
13
|
+
function analyser(css) {
|
|
14
|
+
const noeuds = [];
|
|
15
|
+
let i = 0;
|
|
16
|
+
let debut = 0;
|
|
17
|
+
function avancer() {
|
|
18
|
+
const c = css[i];
|
|
19
|
+
if (c === "\\") {
|
|
20
|
+
i += 2;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (c === "/" && css[i + 1] === "*") {
|
|
24
|
+
const fin = css.indexOf("*/", i + 2);
|
|
25
|
+
i = fin === -1 ? css.length : fin + 2;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (c === '"' || c === "'") {
|
|
29
|
+
i += 1;
|
|
30
|
+
while (i < css.length && css[i] !== c) {
|
|
31
|
+
i += css[i] === "\\" ? 2 : 1;
|
|
32
|
+
}
|
|
33
|
+
i += 1;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
i += 1;
|
|
37
|
+
}
|
|
38
|
+
while (i < css.length) {
|
|
39
|
+
const c = css[i];
|
|
40
|
+
if (c === "{") {
|
|
41
|
+
const prelude = css.slice(debut, i).trim();
|
|
42
|
+
let profondeur = 1;
|
|
43
|
+
i += 1;
|
|
44
|
+
const debutCorps = i;
|
|
45
|
+
while (i < css.length && profondeur > 0) {
|
|
46
|
+
const d = css[i];
|
|
47
|
+
if (d === "{") {
|
|
48
|
+
profondeur += 1;
|
|
49
|
+
i += 1;
|
|
50
|
+
} else if (d === "}") {
|
|
51
|
+
profondeur -= 1;
|
|
52
|
+
i += 1;
|
|
53
|
+
} else {
|
|
54
|
+
avancer();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const corps = css.slice(debutCorps, i - 1);
|
|
58
|
+
if (prelude.startsWith("@")) {
|
|
59
|
+
const nom = /^@([a-zA-Z-]+)/.exec(prelude)?.[1] ?? "";
|
|
60
|
+
noeuds.push(
|
|
61
|
+
GROUPES.has(nom) ? { sorte: "groupe", prelude, enfants: analyser(corps) } : (
|
|
62
|
+
// `@keyframes`, `@font-face`, `@property` : recopies tels quels.
|
|
63
|
+
{ sorte: "brut", texte: `${prelude}{${corps}}` }
|
|
64
|
+
)
|
|
65
|
+
);
|
|
66
|
+
} else if (prelude.length > 0) {
|
|
67
|
+
noeuds.push({ sorte: "regle", selecteur: prelude, corps });
|
|
68
|
+
}
|
|
69
|
+
debut = i;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (c === ";" && css.slice(debut, i).trim().startsWith("@")) {
|
|
73
|
+
noeuds.push({ sorte: "brut", texte: `${css.slice(debut, i).trim()};` });
|
|
74
|
+
i += 1;
|
|
75
|
+
debut = i;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
avancer();
|
|
79
|
+
}
|
|
80
|
+
return noeuds;
|
|
81
|
+
}
|
|
82
|
+
function ecrire(noeuds) {
|
|
83
|
+
return noeuds.map((n) => {
|
|
84
|
+
if (n.sorte === "brut") return n.texte;
|
|
85
|
+
if (n.sorte === "regle") return `${n.selecteur}{${n.corps}}`;
|
|
86
|
+
return `${n.prelude}{${ecrire(n.enfants)}}`;
|
|
87
|
+
}).join("\n");
|
|
88
|
+
}
|
|
89
|
+
function classesDe(selecteur) {
|
|
90
|
+
const classes = [];
|
|
91
|
+
const motif = /(?<!\\)\.((?:\\.|[\w-])+)/g;
|
|
92
|
+
for (const trouve of selecteur.matchAll(motif)) {
|
|
93
|
+
classes.push(trouve[1].replaceAll(/\\(.)/g, "$1"));
|
|
94
|
+
}
|
|
95
|
+
return classes;
|
|
96
|
+
}
|
|
97
|
+
function motsDe(texte) {
|
|
98
|
+
const mots = /* @__PURE__ */ new Set();
|
|
99
|
+
const motif = /[A-Za-z0-9_][A-Za-z0-9_:/.[\]%@-]*/g;
|
|
100
|
+
for (const trouve of texte.matchAll(motif)) mots.add(trouve[0]);
|
|
101
|
+
return mots;
|
|
102
|
+
}
|
|
103
|
+
function estUtilitaire(classe, prefixe) {
|
|
104
|
+
const nue = classe.slice(classe.lastIndexOf(":") + 1);
|
|
105
|
+
return nue.startsWith(prefixe);
|
|
106
|
+
}
|
|
107
|
+
function gardee(classe, employes, sauvegarde) {
|
|
108
|
+
if (employes.has(classe)) return true;
|
|
109
|
+
for (const regle of sauvegarde) {
|
|
110
|
+
if (typeof regle === "string" ? regle === classe : regle.test(classe)) return true;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
function elaguer(css, sources, options = {}) {
|
|
115
|
+
const prefixe = options.prefixe ?? "o-";
|
|
116
|
+
const sauvegarde = options.sauvegarde ?? [];
|
|
117
|
+
const employes = /* @__PURE__ */ new Set();
|
|
118
|
+
for (const source of sources) {
|
|
119
|
+
for (const mot of motsDe(source)) employes.add(mot);
|
|
120
|
+
}
|
|
121
|
+
const vues = /* @__PURE__ */ new Set();
|
|
122
|
+
const survivantes = /* @__PURE__ */ new Set();
|
|
123
|
+
function filtrerSelecteur(selecteur) {
|
|
124
|
+
const retenus = decouperSelecteurs(selecteur).filter((un) => {
|
|
125
|
+
const utilitaires = classesDe(un).filter((c) => estUtilitaire(c, prefixe));
|
|
126
|
+
if (utilitaires.length === 0) return true;
|
|
127
|
+
for (const classe of utilitaires) vues.add(classe);
|
|
128
|
+
const survit = utilitaires.every((c) => gardee(c, employes, sauvegarde));
|
|
129
|
+
if (survit) for (const classe of utilitaires) survivantes.add(classe);
|
|
130
|
+
return survit;
|
|
131
|
+
});
|
|
132
|
+
return retenus.length === 0 ? void 0 : retenus.join(",");
|
|
133
|
+
}
|
|
134
|
+
function filtrer(noeuds) {
|
|
135
|
+
const retenus = [];
|
|
136
|
+
for (const noeud of noeuds) {
|
|
137
|
+
if (noeud.sorte === "brut") {
|
|
138
|
+
retenus.push(noeud);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (noeud.sorte === "regle") {
|
|
142
|
+
const selecteur = filtrerSelecteur(noeud.selecteur);
|
|
143
|
+
if (selecteur !== void 0) retenus.push({ ...noeud, selecteur });
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const enfants = filtrer(noeud.enfants);
|
|
147
|
+
if (enfants.length > 0) retenus.push({ ...noeud, enfants });
|
|
148
|
+
}
|
|
149
|
+
return retenus;
|
|
150
|
+
}
|
|
151
|
+
const elaguee = ecrire(filtrer(analyser(css)));
|
|
152
|
+
return {
|
|
153
|
+
css: elaguee,
|
|
154
|
+
octetsAvant: Buffer.byteLength(css),
|
|
155
|
+
octetsApres: Buffer.byteLength(elaguee),
|
|
156
|
+
gardees: survivantes.size,
|
|
157
|
+
retirees: vues.size - survivantes.size
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function decouperSelecteurs(selecteur) {
|
|
161
|
+
const parts = [];
|
|
162
|
+
let profondeur = 0;
|
|
163
|
+
let debut = 0;
|
|
164
|
+
let i = 0;
|
|
165
|
+
while (i < selecteur.length) {
|
|
166
|
+
const c = selecteur[i];
|
|
167
|
+
if (c === "\\") {
|
|
168
|
+
i += 2;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (c === '"' || c === "'") {
|
|
172
|
+
i += 1;
|
|
173
|
+
while (i < selecteur.length && selecteur[i] !== c) {
|
|
174
|
+
i += selecteur[i] === "\\" ? 2 : 1;
|
|
175
|
+
}
|
|
176
|
+
i += 1;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (c === "(" || c === "[") profondeur += 1;
|
|
180
|
+
else if (c === ")" || c === "]") profondeur -= 1;
|
|
181
|
+
else if (c === "," && profondeur === 0) {
|
|
182
|
+
parts.push(selecteur.slice(debut, i).trim());
|
|
183
|
+
debut = i + 1;
|
|
184
|
+
}
|
|
185
|
+
i += 1;
|
|
186
|
+
}
|
|
187
|
+
parts.push(selecteur.slice(debut).trim());
|
|
188
|
+
return parts.filter((p) => p.length > 0);
|
|
189
|
+
}
|
|
190
|
+
var FOURNISSEUR = "@odoro-cli/libs/generateur";
|
|
191
|
+
async function fournisseurDe(root) {
|
|
192
|
+
try {
|
|
193
|
+
const exiger = createRequire(join(root, "package.json"));
|
|
194
|
+
const chemin = exiger.resolve(FOURNISSEUR);
|
|
195
|
+
const module_ = await import(pathToFileURL(chemin).href);
|
|
196
|
+
if (typeof module_.renderUtilitairesPour !== "function") return void 0;
|
|
197
|
+
return { renderUtilitairesPour: module_.renderUtilitairesPour };
|
|
198
|
+
} catch {
|
|
199
|
+
return void 0;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/build/build.ts
|
|
204
|
+
function toPosix(path) {
|
|
205
|
+
return path.split(sep).join("/");
|
|
206
|
+
}
|
|
207
|
+
function buildEnv(config) {
|
|
208
|
+
const env = {
|
|
209
|
+
MODE: "production",
|
|
210
|
+
DEV: false,
|
|
211
|
+
PROD: true,
|
|
212
|
+
BASE_URL: config.base
|
|
213
|
+
};
|
|
214
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
215
|
+
if (key.startsWith(config.envPrefix) && value !== void 0) env[key] = value;
|
|
216
|
+
}
|
|
217
|
+
return env;
|
|
218
|
+
}
|
|
219
|
+
function outputsForEntry(result, entry, outDir, root) {
|
|
220
|
+
const toRelative = (file) => toPosix(relative(outDir, resolve(root, file)));
|
|
221
|
+
for (const [file, meta] of Object.entries(result.metafile.outputs)) {
|
|
222
|
+
if (meta.entryPoint === void 0) continue;
|
|
223
|
+
if (resolve(root, meta.entryPoint) !== resolve(entry)) continue;
|
|
224
|
+
if (!file.endsWith(".js")) continue;
|
|
225
|
+
return {
|
|
226
|
+
script: toRelative(file),
|
|
227
|
+
styles: meta.cssBundle === void 0 ? [] : [toRelative(meta.cssBundle)]
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return { script: void 0, styles: [] };
|
|
231
|
+
}
|
|
232
|
+
async function taillerFeuilles(result, config, html) {
|
|
233
|
+
const produits = Object.keys(result.metafile.outputs).map((f) => resolve(config.root, f));
|
|
234
|
+
const feuilles = produits.filter((f) => f.endsWith(".css"));
|
|
235
|
+
if (feuilles.length === 0) return;
|
|
236
|
+
const sources = [html];
|
|
237
|
+
for (const fichier of produits) {
|
|
238
|
+
if (fichier.endsWith(".js")) sources.push(await readFile(fichier, "utf8"));
|
|
239
|
+
}
|
|
240
|
+
const fournisseur = await fournisseurDe(config.root);
|
|
241
|
+
const employes = /* @__PURE__ */ new Set();
|
|
242
|
+
if (fournisseur !== void 0) {
|
|
243
|
+
for (const source of sources) for (const mot of motsDe(source)) employes.add(mot);
|
|
244
|
+
for (const garde of config.build.safelist) {
|
|
245
|
+
if (typeof garde === "string") employes.add(garde);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
for (const feuille of feuilles) {
|
|
249
|
+
const avant = await readFile(feuille, "utf8");
|
|
250
|
+
if (fournisseur === void 0) {
|
|
251
|
+
const rapport = elaguer(avant, sources, { sauvegarde: config.build.safelist });
|
|
252
|
+
await writeFile(feuille, rapport.css, "utf8");
|
|
253
|
+
info(
|
|
254
|
+
` ${colors.dim("elagage")} ${basename(feuille)} ${size(rapport.octetsAvant)} \u2192 ${size(rapport.octetsApres)} ${colors.dim(`${String(rapport.gardees)} classes gardees`)}`
|
|
255
|
+
);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const socle = elaguer(avant, [], {}).css;
|
|
259
|
+
const utilitaires = fournisseur.renderUtilitairesPour(employes);
|
|
260
|
+
const apres = `${socle}
|
|
261
|
+
${utilitaires}`;
|
|
262
|
+
await writeFile(feuille, apres, "utf8");
|
|
263
|
+
info(
|
|
264
|
+
` ${colors.dim("generation")} ${basename(feuille)} ${size(Buffer.byteLength(avant))} \u2192 ${size(Buffer.byteLength(apres))}`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async function buildProject(config) {
|
|
269
|
+
const started = Date.now();
|
|
270
|
+
const indexFile = join(config.root, "index.html");
|
|
271
|
+
if (!existsSync(indexFile)) {
|
|
272
|
+
throw new Error(`[odoro] Aucun "index.html" a la racine du projet (${config.root}).`);
|
|
273
|
+
}
|
|
274
|
+
const html = await readFile(indexFile, "utf8");
|
|
275
|
+
const entries = extractEntries(html, config.root);
|
|
276
|
+
if (entries.length === 0) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
`[odoro] Aucun point d'entree : "index.html" doit contenir un <script type="module" src="...">.`
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
await rm(config.outDir, { recursive: true, force: true });
|
|
282
|
+
await mkdir(config.outDir, { recursive: true });
|
|
283
|
+
const result = await build({
|
|
284
|
+
entryPoints: [...entries],
|
|
285
|
+
bundle: true,
|
|
286
|
+
format: "esm",
|
|
287
|
+
platform: "browser",
|
|
288
|
+
target: config.build.target,
|
|
289
|
+
splitting: true,
|
|
290
|
+
minify: config.build.minify,
|
|
291
|
+
sourcemap: config.build.sourcemap,
|
|
292
|
+
metafile: true,
|
|
293
|
+
outdir: join(config.outDir, "assets"),
|
|
294
|
+
absWorkingDir: config.root,
|
|
295
|
+
publicPath: `${config.base}assets`,
|
|
296
|
+
// Les empreintes rendent les fichiers immuables : ils peuvent etre mis en
|
|
297
|
+
// cache indefiniment, et un deploiement n'invalide que ce qui a change.
|
|
298
|
+
entryNames: "[name]-[hash]",
|
|
299
|
+
chunkNames: "chunk-[hash]",
|
|
300
|
+
assetNames: "[name]-[hash]",
|
|
301
|
+
jsx: "automatic",
|
|
302
|
+
logLevel: "silent",
|
|
303
|
+
define: {
|
|
304
|
+
"import.meta.env": JSON.stringify(buildEnv(config)),
|
|
305
|
+
"process.env.NODE_ENV": JSON.stringify("production"),
|
|
306
|
+
...config.define
|
|
307
|
+
},
|
|
308
|
+
loader: {
|
|
309
|
+
".svg": "file",
|
|
310
|
+
".png": "file",
|
|
311
|
+
".jpg": "file",
|
|
312
|
+
".jpeg": "file",
|
|
313
|
+
".gif": "file",
|
|
314
|
+
".webp": "file",
|
|
315
|
+
".avif": "file",
|
|
316
|
+
".ico": "file",
|
|
317
|
+
".woff": "file",
|
|
318
|
+
".woff2": "file",
|
|
319
|
+
".mp4": "file",
|
|
320
|
+
".webm": "file"
|
|
321
|
+
},
|
|
322
|
+
plugins: [
|
|
323
|
+
{
|
|
324
|
+
name: "odoro-alias",
|
|
325
|
+
setup(builder) {
|
|
326
|
+
builder.onResolve({ filter: /.*/ }, (args) => {
|
|
327
|
+
if (args.kind === "entry-point") return null;
|
|
328
|
+
const aliased = applyAlias(args.path, config);
|
|
329
|
+
if (aliased === args.path || isBareSpecifier(aliased)) return null;
|
|
330
|
+
return builder.resolve(aliased, {
|
|
331
|
+
kind: "import-statement",
|
|
332
|
+
resolveDir: args.resolveDir,
|
|
333
|
+
importer: args.importer,
|
|
334
|
+
pluginData: { aliased: true }
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
]
|
|
340
|
+
});
|
|
341
|
+
if (config.build.elaguer) await taillerFeuilles(result, config, html);
|
|
342
|
+
let output = html;
|
|
343
|
+
for (const entry of entries) {
|
|
344
|
+
const { script, styles } = outputsForEntry(
|
|
345
|
+
result,
|
|
346
|
+
entry,
|
|
347
|
+
join(config.outDir, "assets"),
|
|
348
|
+
config.root
|
|
349
|
+
);
|
|
350
|
+
if (script === void 0) continue;
|
|
351
|
+
const original = new RegExp(
|
|
352
|
+
`<script[^>]*type=["']module["'][^>]*src=["'][^"']*${basename(entry)}["'][^>]*></script>`,
|
|
353
|
+
"i"
|
|
354
|
+
);
|
|
355
|
+
const tags = [
|
|
356
|
+
...styles.map(
|
|
357
|
+
(style) => `<link rel="stylesheet" href="${config.base}assets/${style}">`
|
|
358
|
+
),
|
|
359
|
+
`<script type="module" crossorigin src="${config.base}assets/${script}"></script>`
|
|
360
|
+
].join("\n ");
|
|
361
|
+
output = output.replace(original, tags);
|
|
362
|
+
}
|
|
363
|
+
await writeFile(join(config.outDir, "index.html"), output, "utf8");
|
|
364
|
+
if (existsSync(config.publicDir)) {
|
|
365
|
+
await cp(config.publicDir, config.outDir, { recursive: true });
|
|
366
|
+
}
|
|
367
|
+
const files = (await Promise.all(
|
|
368
|
+
Object.keys(result.metafile.outputs).map(async (file) => {
|
|
369
|
+
const chemin = resolve(config.root, file);
|
|
370
|
+
return {
|
|
371
|
+
path: toPosix(relative(config.outDir, chemin)),
|
|
372
|
+
bytes: (await stat(chemin)).size
|
|
373
|
+
};
|
|
374
|
+
})
|
|
375
|
+
)).sort((a, b) => b.bytes - a.bytes);
|
|
376
|
+
return { outDir: config.outDir, files, elapsed: Date.now() - started };
|
|
377
|
+
}
|
|
378
|
+
function reportBuild(output, root) {
|
|
379
|
+
const directory = toPosix(relative(root, output.outDir)) || ".";
|
|
380
|
+
let total = 0;
|
|
381
|
+
for (const file of output.files) {
|
|
382
|
+
total += file.bytes;
|
|
383
|
+
if (file.path.endsWith(".map")) continue;
|
|
384
|
+
info(
|
|
385
|
+
` ${colors.dim(`${directory}/`)}${file.path} ${colors.dim(size(file.bytes))}`
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
success(`compile en ${duration(output.elapsed)} \u2014 ${size(total)} au total`);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export { buildProject, reportBuild };
|
|
@@ -78,7 +78,11 @@ async function loadConfig(root, overrides = {}) {
|
|
|
78
78
|
outDir: merged.build?.outDir ?? "dist",
|
|
79
79
|
minify: merged.build?.minify ?? true,
|
|
80
80
|
sourcemap: merged.build?.sourcemap ?? true,
|
|
81
|
-
target: merged.build?.target ?? "es2022"
|
|
81
|
+
target: merged.build?.target ?? "es2022",
|
|
82
|
+
// Actif par defaut : une feuille qui contient toutes les classes
|
|
83
|
+
// possibles est un accident de generation, pas une intention.
|
|
84
|
+
elaguer: merged.build?.elaguer ?? true,
|
|
85
|
+
safelist: merged.build?.safelist ?? []
|
|
82
86
|
},
|
|
83
87
|
// Les alias declares dans `tsconfig.json` sont repris d'office. Sans
|
|
84
88
|
// cela, un projet qui suit `odoro init` — lequel deduit son prefixe du
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { loadConfig } from './chunk-
|
|
2
|
+
import { loadConfig } from './chunk-Q35CGY2Z.js';
|
|
3
3
|
import './chunk-T6RLHSCW.js';
|
|
4
4
|
import { error } from './chunk-JMEHF3KN.js';
|
|
5
5
|
import { realpathSync } from 'fs';
|
|
@@ -69,7 +69,7 @@ ${colors.bold("Base de donnees")}
|
|
|
69
69
|
db:status Liste les bases du projet
|
|
70
70
|
db:create Provisionne une base et ecrit .env
|
|
71
71
|
db:branch Cree une previsualisation par branche
|
|
72
|
-
${colors.dim("Ces commandes demandent @odoro/cloud-sdk, installe a part :")}
|
|
72
|
+
${colors.dim("Ces commandes demandent @odoro-cli/cloud-sdk, installe a part :")}
|
|
73
73
|
${colors.dim("il ne vient pas avec, ce binaire etant telecharge a chaque")}
|
|
74
74
|
${colors.dim("creation de projet.")}
|
|
75
75
|
|
|
@@ -130,7 +130,7 @@ function rootFrom(flags, positional) {
|
|
|
130
130
|
async function run(argv) {
|
|
131
131
|
const { command, positional, flags } = parseArgs(argv);
|
|
132
132
|
if (flags["version"] === true) {
|
|
133
|
-
const manifest = await import('./package-
|
|
133
|
+
const manifest = await import('./package-VRJRDQY3.js');
|
|
134
134
|
console.log(manifest.default.version);
|
|
135
135
|
return 0;
|
|
136
136
|
}
|
|
@@ -141,7 +141,7 @@ async function run(argv) {
|
|
|
141
141
|
switch (command) {
|
|
142
142
|
case "create":
|
|
143
143
|
case "new": {
|
|
144
|
-
const { createCommand } = await import('./create-
|
|
144
|
+
const { createCommand } = await import('./create-XJM3TUAQ.js');
|
|
145
145
|
const options = {};
|
|
146
146
|
if (positional[0] !== void 0) options.name = positional[0];
|
|
147
147
|
if (typeof flags["template"] === "string") options.template = flags["template"];
|
|
@@ -160,7 +160,7 @@ async function run(argv) {
|
|
|
160
160
|
return new Promise(() => void 0);
|
|
161
161
|
}
|
|
162
162
|
case "build": {
|
|
163
|
-
const { buildProject, reportBuild } = await import('./build-
|
|
163
|
+
const { buildProject, reportBuild } = await import('./build-J2J3NBV5.js');
|
|
164
164
|
const config = await loadConfig(rootFrom(flags, positional), overridesFrom(flags));
|
|
165
165
|
const output = await buildProject(config);
|
|
166
166
|
reportBuild(output, process.cwd());
|
|
@@ -194,7 +194,7 @@ async function run(argv) {
|
|
|
194
194
|
case "db:status":
|
|
195
195
|
case "db:create":
|
|
196
196
|
case "db:branch": {
|
|
197
|
-
const db = await import('./commands-
|
|
197
|
+
const db = await import('./commands-4JRBD55Z.js');
|
|
198
198
|
const options = {
|
|
199
199
|
root: typeof flags["root"] === "string" ? flags["root"] : process.cwd(),
|
|
200
200
|
...typeof flags["api"] === "string" ? { apiUrl: flags["api"] } : {},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { writeDatabaseUrl, assertEnvIgnored } from './chunk-
|
|
2
|
+
import { writeDatabaseUrl, assertEnvIgnored } from './chunk-3SZIN6VG.js';
|
|
3
3
|
import { info, success, colors, warn, error } from './chunk-JMEHF3KN.js';
|
|
4
4
|
import { randomBytes } from 'crypto';
|
|
5
5
|
import { readFile, mkdir, writeFile, chmod } from 'fs/promises';
|
|
@@ -58,7 +58,7 @@ async function findToken(apiUrl, path = configPath(), env = processEnv()) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
// src/db/sdk.ts
|
|
61
|
-
var SDK_PACKAGE = "@odoro/cloud-sdk";
|
|
61
|
+
var SDK_PACKAGE = "@odoro-cli/cloud-sdk";
|
|
62
62
|
async function loadSdk() {
|
|
63
63
|
try {
|
|
64
64
|
const specifier = SDK_PACKAGE;
|
|
@@ -78,7 +78,7 @@ creation de projet, et la plupart n'emploient pas la plateforme.`
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
// src/db/commands.ts
|
|
81
|
-
var DEFAULT_API_URL = "https://
|
|
81
|
+
var DEFAULT_API_URL = "https://db.odoro.dev";
|
|
82
82
|
function idempotencyKey() {
|
|
83
83
|
return randomBytes(24).toString("hex");
|
|
84
84
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { writeDatabaseUrl, assertEnvIgnored, PROVIDER_PENDING, checkDatabaseUrl } from './chunk-
|
|
2
|
+
import { writeDatabaseUrl, assertEnvIgnored, PROVIDER_PENDING, checkDatabaseUrl } from './chunk-3SZIN6VG.js';
|
|
3
3
|
import { execSync } from 'child_process';
|
|
4
4
|
import { existsSync, statSync, readdirSync, readFileSync } from 'fs';
|
|
5
5
|
import { resolve, basename, dirname, join } from 'path';
|
package/dist/index.d.ts
CHANGED
|
@@ -31,6 +31,31 @@ interface BuildConfig {
|
|
|
31
31
|
sourcemap?: boolean;
|
|
32
32
|
/** Cible de compilation. @defaultValue 'es2022' */
|
|
33
33
|
target?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Retire de la feuille de style les classes utilitaires que rien n'emploie.
|
|
36
|
+
*
|
|
37
|
+
* La bibliotheque livre une feuille pre-generee qui les contient toutes ;
|
|
38
|
+
* une application donnee en emploie une fraction. L'elagage lit le code
|
|
39
|
+
* **produit** — donc les composants de bibliotheque autant que la source de
|
|
40
|
+
* l'application — et ne garde que ce qui est atteignable.
|
|
41
|
+
*
|
|
42
|
+
* Une classe assemblee a l'execution (`o-text-${couleur}`) n'existe nulle
|
|
43
|
+
* part sous sa forme finale et disparaitra : la declarer dans
|
|
44
|
+
* `safelist` est le seul moyen de la garder.
|
|
45
|
+
*
|
|
46
|
+
* @defaultValue true
|
|
47
|
+
*/
|
|
48
|
+
elaguer?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Les classes gardees quoi qu'il arrive, malgre l'elagage.
|
|
51
|
+
*
|
|
52
|
+
* Une chaine garde une classe ; une expression reguliere garde toutes celles
|
|
53
|
+
* qu'elle reconnait.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* { safelist: [/^o-text-/, 'o-animate-spin'] }
|
|
57
|
+
*/
|
|
58
|
+
safelist?: readonly (string | RegExp)[];
|
|
34
59
|
}
|
|
35
60
|
/** Configuration d'un projet Odoro. */
|
|
36
61
|
interface OdoroConfig {
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export { defineConfig, loadConfig } from './chunk-
|
|
2
|
+
export { defineConfig, loadConfig } from './chunk-Q35CGY2Z.js';
|
|
3
3
|
import './chunk-T6RLHSCW.js';
|
|
4
|
-
export { buildProject, reportBuild } from './chunk-
|
|
4
|
+
export { buildProject, reportBuild } from './chunk-JGXJBVI7.js';
|
|
5
5
|
export { ModuleGraph, depFileName, detectSelfAccepting, optimizeDeps, scanDependencies, startDevServer } from './chunk-G5QXVBYT.js';
|
|
6
6
|
export { startPreviewServer } from './chunk-2YDI5NKV.js';
|
|
7
7
|
import './chunk-JMEHF3KN.js';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// package.json
|
|
3
3
|
var package_default = {
|
|
4
4
|
name: "odoro",
|
|
5
|
-
version: "0.1.
|
|
5
|
+
version: "0.1.4",
|
|
6
6
|
type: "module",
|
|
7
7
|
license: "UNLICENSED",
|
|
8
8
|
author: "BouBouw",
|
|
@@ -56,6 +56,15 @@ var package_default = {
|
|
|
56
56
|
typescript: "^5.9.3",
|
|
57
57
|
vitest: "^3.2.4",
|
|
58
58
|
zod: "^4.5.1"
|
|
59
|
+
},
|
|
60
|
+
homepage: "https://odoro.dev",
|
|
61
|
+
repository: {
|
|
62
|
+
type: "git",
|
|
63
|
+
url: "git+https://github.com/ODORO-CLI/OdoroKit.git",
|
|
64
|
+
directory: "packages/odoro"
|
|
65
|
+
},
|
|
66
|
+
bugs: {
|
|
67
|
+
url: "https://github.com/ODORO-CLI/OdoroKit/issues"
|
|
59
68
|
}
|
|
60
69
|
};
|
|
61
70
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "odoro",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "BouBouw",
|
|
@@ -48,6 +48,15 @@
|
|
|
48
48
|
"vitest": "^3.2.4",
|
|
49
49
|
"zod": "^4.5.1"
|
|
50
50
|
},
|
|
51
|
+
"homepage": "https://odoro.dev",
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/ODORO-CLI/OdoroKit.git",
|
|
55
|
+
"directory": "packages/odoro"
|
|
56
|
+
},
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/ODORO-CLI/OdoroKit/issues"
|
|
59
|
+
},
|
|
51
60
|
"scripts": {
|
|
52
61
|
"build": "tsup",
|
|
53
62
|
"dev": "tsup --watch",
|
|
@@ -34,13 +34,12 @@ Toutes les valeurs visuelles passent par des variables CSS. Surcharger
|
|
|
34
34
|
`--o-palette-brand-600` dans `src/styles.css` retheme l'application **et** les
|
|
35
35
|
composants de la librairie, sans toucher a leur code.
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
Une seule feuille de style : `@odoro-cli/libs/styles.css`, a importer une fois
|
|
38
|
+
a la racine de l'application.
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
Importer l'une **ou** l'autre, jamais les deux.
|
|
40
|
+
Elle pese 1,7 Mo sur le disque, mais ce n'est pas ce que vos visiteurs
|
|
41
|
+
telechargent : la compilation n'en garde que les classes que votre code
|
|
42
|
+
emploie reellement — quelques dizaines de kilooctets en pratique.
|
|
44
43
|
|
|
45
44
|
## Rechargement a chaud
|
|
46
45
|
|
package/dist/chunk-UGRSODHU.js
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { extractEntries, applyAlias, isBareSpecifier } from './chunk-G5QXVBYT.js';
|
|
3
|
-
import { info, colors, size, success, duration } from './chunk-JMEHF3KN.js';
|
|
4
|
-
import { existsSync } from 'fs';
|
|
5
|
-
import { readFile, rm, mkdir, writeFile, cp } from 'fs/promises';
|
|
6
|
-
import { join, basename, relative, resolve, sep } from 'path';
|
|
7
|
-
import { build } from 'esbuild';
|
|
8
|
-
|
|
9
|
-
function toPosix(path) {
|
|
10
|
-
return path.split(sep).join("/");
|
|
11
|
-
}
|
|
12
|
-
function buildEnv(config) {
|
|
13
|
-
const env = {
|
|
14
|
-
MODE: "production",
|
|
15
|
-
DEV: false,
|
|
16
|
-
PROD: true,
|
|
17
|
-
BASE_URL: config.base
|
|
18
|
-
};
|
|
19
|
-
for (const [key, value] of Object.entries(process.env)) {
|
|
20
|
-
if (key.startsWith(config.envPrefix) && value !== void 0) env[key] = value;
|
|
21
|
-
}
|
|
22
|
-
return env;
|
|
23
|
-
}
|
|
24
|
-
function outputsForEntry(result, entry, outDir, root) {
|
|
25
|
-
const toRelative = (file) => toPosix(relative(outDir, resolve(root, file)));
|
|
26
|
-
for (const [file, meta] of Object.entries(result.metafile.outputs)) {
|
|
27
|
-
if (meta.entryPoint === void 0) continue;
|
|
28
|
-
if (resolve(root, meta.entryPoint) !== resolve(entry)) continue;
|
|
29
|
-
if (!file.endsWith(".js")) continue;
|
|
30
|
-
return {
|
|
31
|
-
script: toRelative(file),
|
|
32
|
-
styles: meta.cssBundle === void 0 ? [] : [toRelative(meta.cssBundle)]
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
return { script: void 0, styles: [] };
|
|
36
|
-
}
|
|
37
|
-
async function buildProject(config) {
|
|
38
|
-
const started = Date.now();
|
|
39
|
-
const indexFile = join(config.root, "index.html");
|
|
40
|
-
if (!existsSync(indexFile)) {
|
|
41
|
-
throw new Error(`[odoro] Aucun "index.html" a la racine du projet (${config.root}).`);
|
|
42
|
-
}
|
|
43
|
-
const html = await readFile(indexFile, "utf8");
|
|
44
|
-
const entries = extractEntries(html, config.root);
|
|
45
|
-
if (entries.length === 0) {
|
|
46
|
-
throw new Error(
|
|
47
|
-
`[odoro] Aucun point d'entree : "index.html" doit contenir un <script type="module" src="...">.`
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
await rm(config.outDir, { recursive: true, force: true });
|
|
51
|
-
await mkdir(config.outDir, { recursive: true });
|
|
52
|
-
const result = await build({
|
|
53
|
-
entryPoints: [...entries],
|
|
54
|
-
bundle: true,
|
|
55
|
-
format: "esm",
|
|
56
|
-
platform: "browser",
|
|
57
|
-
target: config.build.target,
|
|
58
|
-
splitting: true,
|
|
59
|
-
minify: config.build.minify,
|
|
60
|
-
sourcemap: config.build.sourcemap,
|
|
61
|
-
metafile: true,
|
|
62
|
-
outdir: join(config.outDir, "assets"),
|
|
63
|
-
absWorkingDir: config.root,
|
|
64
|
-
publicPath: `${config.base}assets`,
|
|
65
|
-
// Les empreintes rendent les fichiers immuables : ils peuvent etre mis en
|
|
66
|
-
// cache indefiniment, et un deploiement n'invalide que ce qui a change.
|
|
67
|
-
entryNames: "[name]-[hash]",
|
|
68
|
-
chunkNames: "chunk-[hash]",
|
|
69
|
-
assetNames: "[name]-[hash]",
|
|
70
|
-
jsx: "automatic",
|
|
71
|
-
logLevel: "silent",
|
|
72
|
-
define: {
|
|
73
|
-
"import.meta.env": JSON.stringify(buildEnv(config)),
|
|
74
|
-
"process.env.NODE_ENV": JSON.stringify("production"),
|
|
75
|
-
...config.define
|
|
76
|
-
},
|
|
77
|
-
loader: {
|
|
78
|
-
".svg": "file",
|
|
79
|
-
".png": "file",
|
|
80
|
-
".jpg": "file",
|
|
81
|
-
".jpeg": "file",
|
|
82
|
-
".gif": "file",
|
|
83
|
-
".webp": "file",
|
|
84
|
-
".avif": "file",
|
|
85
|
-
".ico": "file",
|
|
86
|
-
".woff": "file",
|
|
87
|
-
".woff2": "file",
|
|
88
|
-
".mp4": "file",
|
|
89
|
-
".webm": "file"
|
|
90
|
-
},
|
|
91
|
-
plugins: [
|
|
92
|
-
{
|
|
93
|
-
name: "odoro-alias",
|
|
94
|
-
setup(builder) {
|
|
95
|
-
builder.onResolve({ filter: /.*/ }, (args) => {
|
|
96
|
-
if (args.kind === "entry-point") return null;
|
|
97
|
-
const aliased = applyAlias(args.path, config);
|
|
98
|
-
if (aliased === args.path || isBareSpecifier(aliased)) return null;
|
|
99
|
-
return builder.resolve(aliased, {
|
|
100
|
-
kind: "import-statement",
|
|
101
|
-
resolveDir: args.resolveDir,
|
|
102
|
-
importer: args.importer,
|
|
103
|
-
pluginData: { aliased: true }
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
]
|
|
109
|
-
});
|
|
110
|
-
let output = html;
|
|
111
|
-
for (const entry of entries) {
|
|
112
|
-
const { script, styles } = outputsForEntry(
|
|
113
|
-
result,
|
|
114
|
-
entry,
|
|
115
|
-
join(config.outDir, "assets"),
|
|
116
|
-
config.root
|
|
117
|
-
);
|
|
118
|
-
if (script === void 0) continue;
|
|
119
|
-
const original = new RegExp(
|
|
120
|
-
`<script[^>]*type=["']module["'][^>]*src=["'][^"']*${basename(entry)}["'][^>]*></script>`,
|
|
121
|
-
"i"
|
|
122
|
-
);
|
|
123
|
-
const tags = [
|
|
124
|
-
...styles.map(
|
|
125
|
-
(style) => `<link rel="stylesheet" href="${config.base}assets/${style}">`
|
|
126
|
-
),
|
|
127
|
-
`<script type="module" crossorigin src="${config.base}assets/${script}"></script>`
|
|
128
|
-
].join("\n ");
|
|
129
|
-
output = output.replace(original, tags);
|
|
130
|
-
}
|
|
131
|
-
await writeFile(join(config.outDir, "index.html"), output, "utf8");
|
|
132
|
-
if (existsSync(config.publicDir)) {
|
|
133
|
-
await cp(config.publicDir, config.outDir, { recursive: true });
|
|
134
|
-
}
|
|
135
|
-
const files = Object.entries(result.metafile.outputs).map(([file, meta]) => ({
|
|
136
|
-
path: toPosix(relative(config.outDir, resolve(config.root, file))),
|
|
137
|
-
bytes: meta.bytes
|
|
138
|
-
})).sort((a, b) => b.bytes - a.bytes);
|
|
139
|
-
return { outDir: config.outDir, files, elapsed: Date.now() - started };
|
|
140
|
-
}
|
|
141
|
-
function reportBuild(output, root) {
|
|
142
|
-
const directory = toPosix(relative(root, output.outDir)) || ".";
|
|
143
|
-
let total = 0;
|
|
144
|
-
for (const file of output.files) {
|
|
145
|
-
total += file.bytes;
|
|
146
|
-
if (file.path.endsWith(".map")) continue;
|
|
147
|
-
info(
|
|
148
|
-
` ${colors.dim(`${directory}/`)}${file.path} ${colors.dim(size(file.bytes))}`
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
success(`compile en ${duration(output.elapsed)} \u2014 ${size(total)} au total`);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export { buildProject, reportBuild };
|