odoro 1.0.9 → 2.1.0
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 +94 -0
- package/client.d.ts +90 -16
- package/dist/build-XJIP4GNT.js +5 -0
- package/dist/{chunk-34RGOZFA.js → chunk-4D56Z7G3.js} +49 -50
- package/dist/{chunk-JMEHF3KN.js → chunk-7ZB5MAM6.js} +4 -4
- package/dist/chunk-DF67LADH.js +735 -0
- package/dist/{chunk-LHZGX5ML.js → chunk-HS7NGZLM.js} +307 -196
- package/dist/chunk-LIKVHWB4.js +231 -0
- package/dist/chunk-QOVBLN7A.js +241 -0
- package/dist/chunk-RTWJ5GFB.js +729 -0
- package/dist/chunk-TONYIKCN.js +57 -0
- package/dist/cli.d.ts +12 -13
- package/dist/cli.js +86 -63
- package/dist/{commands-AHMXBWQQ.js → commands-GYA7NTCX.js} +124 -124
- package/dist/commands-UOWN3IK4.js +181 -0
- package/dist/{create-CYVSVDAD.js → create-B2TLV6XI.js} +198 -160
- package/dist/index.d.ts +603 -151
- package/dist/index.js +6 -6
- package/dist/{package-EIDLKAA6.js → package-3ZJOEXM4.js} +6 -5
- package/dist/preview-3IRG7MHV.js +4 -0
- package/dist/registry/index.d.ts +75 -76
- package/dist/registry/index.js +1 -1
- package/dist/{server-4UGN3SFS.js → server-ZB3P3EGS.js} +3 -3
- package/package.json +6 -5
- package/templates/react-ts/README.md +71 -35
- package/templates/react-ts/_env.example +9 -0
- package/templates/react-ts/_variants/with-engine/src/background.tsx +118 -0
- package/templates/react-ts/_variants/without-libs/src/App.tsx +380 -0
- package/templates/react-ts/_variants/without-libs/src/background.tsx +24 -0
- package/templates/react-ts/_variants/without-libs/src/entry-server.tsx +39 -0
- package/templates/react-ts/_variants/without-libs/src/main.tsx +26 -0
- package/templates/{react-ts-server/_variantes/sans-libs/client → react-ts/_variants/without-libs}/src/styles.css +137 -137
- package/templates/react-ts/{_variantes/sans-routeur → _variants/without-router}/src/App.tsx +141 -141
- package/templates/react-ts/_variants/without-router/src/entry-server.tsx +39 -0
- package/templates/react-ts/index.html +4 -4
- package/templates/react-ts/odoro.config.ts +5 -0
- package/templates/react-ts/src/App.tsx +219 -209
- package/templates/react-ts/src/background.tsx +66 -0
- package/templates/react-ts/src/entry-server.tsx +85 -0
- package/templates/react-ts/src/main.tsx +13 -4
- package/templates/react-ts/src/router.tsx +65 -45
- package/templates/react-ts/src/styles.css +5 -5
- package/templates/react-ts-server/Dockerfile +9 -9
- package/templates/react-ts-server/README.md +97 -45
- package/templates/react-ts-server/_env.example +31 -33
- package/templates/react-ts-server/_variants/with-engine/client/src/background.tsx +118 -0
- package/templates/react-ts-server/_variants/without-libs/client/src/App.tsx +380 -0
- package/templates/react-ts-server/_variants/without-libs/client/src/background.tsx +24 -0
- package/templates/react-ts-server/_variants/without-libs/client/src/entry-server.tsx +39 -0
- package/templates/react-ts-server/_variants/without-libs/client/src/main.tsx +26 -0
- package/templates/{react-ts/_variantes/sans-libs → react-ts-server/_variants/without-libs/client}/src/styles.css +137 -137
- package/templates/react-ts-server/{_variantes/sans-routeur → _variants/without-router}/client/src/App.tsx +141 -141
- package/templates/react-ts-server/_variants/without-router/client/src/entry-server.tsx +39 -0
- package/templates/react-ts-server/client/index.html +4 -4
- package/templates/react-ts-server/client/src/App.tsx +228 -210
- package/templates/react-ts-server/client/src/account.tsx +267 -0
- package/templates/react-ts-server/client/src/auth.tsx +139 -0
- package/templates/react-ts-server/client/src/background.tsx +66 -0
- package/templates/react-ts-server/client/src/entry-server.tsx +92 -0
- package/templates/react-ts-server/client/src/main.tsx +17 -5
- package/templates/react-ts-server/client/src/router.tsx +77 -45
- package/templates/react-ts-server/client/src/styles.css +5 -5
- package/templates/react-ts-server/odoro.config.ts +7 -4
- package/templates/react-ts-server/package.json +2 -0
- package/templates/react-ts-server/scripts/dev.mjs +14 -14
- package/templates/react-ts-server/server/src/main.ts +119 -45
- package/templates/react-ts-server/server/src/modules/auth/index.ts +238 -0
- package/templates/react-ts-server/server/src/modules/auth/password.ts +122 -0
- package/templates/react-ts-server/server/src/modules/auth/store.ts +193 -0
- package/templates/react-ts-server/server/src/modules/health/index.ts +45 -46
- package/dist/build-JFQHODAT.js +0 -5
- package/dist/chunk-22KJTV2R.js +0 -380
- package/dist/chunk-3SZIN6VG.js +0 -64
- package/dist/chunk-DL3NPC4H.js +0 -113
- package/dist/chunk-TQUJ3MFS.js +0 -268
- package/dist/chunk-ZVL7EXJO.js +0 -66
- package/dist/commands-4JRBD55Z.js +0 -245
- package/dist/preview-7DKEAQOQ.js +0 -4
- package/templates/react-ts/_variantes/avec-moteur/src/fond.tsx +0 -118
- package/templates/react-ts/_variantes/sans-libs/src/App.tsx +0 -382
- package/templates/react-ts/_variantes/sans-libs/src/fond.tsx +0 -23
- package/templates/react-ts/_variantes/sans-libs/src/main.tsx +0 -17
- package/templates/react-ts/src/fond.tsx +0 -66
- package/templates/react-ts-server/_variantes/avec-moteur/client/src/fond.tsx +0 -118
- package/templates/react-ts-server/_variantes/sans-libs/client/src/App.tsx +0 -382
- package/templates/react-ts-server/_variantes/sans-libs/client/src/fond.tsx +0 -23
- package/templates/react-ts-server/_variantes/sans-libs/client/src/main.tsx +0 -17
- package/templates/react-ts-server/client/src/fond.tsx +0 -66
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { guessAlias, defaultAliases } from './chunk-T6RLHSCW.js';
|
|
3
|
-
import { object, string, _minLength, array, _default, record, literal, safeParse, resolveGraph, describeProblem, parseMeta } from './chunk-
|
|
4
|
-
import { colors, info, warn, success, error } from './chunk-
|
|
3
|
+
import { object, string, _minLength, array, _default, record, literal, safeParse, resolveGraph, describeProblem, parseMeta } from './chunk-4D56Z7G3.js';
|
|
4
|
+
import { colors, info, warn, success, error } from './chunk-7ZB5MAM6.js';
|
|
5
5
|
import { readFile, writeFile, mkdir, rm, rename } from 'fs/promises';
|
|
6
6
|
import { join, isAbsolute, resolve, dirname, posix } from 'path';
|
|
7
7
|
import * as prompts from '@clack/prompts';
|
|
@@ -9,37 +9,37 @@ import { createHash } from 'crypto';
|
|
|
9
9
|
|
|
10
10
|
var CONFIG_FILE = "odoro.json";
|
|
11
11
|
var trackedFileSchema = object({
|
|
12
|
-
/**
|
|
12
|
+
/** Path in the project, relative to its root, in forward slashes. */
|
|
13
13
|
path: string().check(_minLength(1)),
|
|
14
|
-
/**
|
|
14
|
+
/** Hash of the content **as it was delivered**. */
|
|
15
15
|
hash: string().check(_minLength(1))
|
|
16
16
|
});
|
|
17
17
|
var installedSchema = object({
|
|
18
|
-
/**
|
|
18
|
+
/** Installation date, in ISO 8601. */
|
|
19
19
|
installedAt: string().check(_minLength(1)),
|
|
20
|
-
/**
|
|
20
|
+
/** Files written by this entry. */
|
|
21
21
|
files: array(trackedFileSchema)
|
|
22
22
|
});
|
|
23
23
|
var aliasesSchema = object({
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* Import prefix used in the written code, without a trailing slash. For
|
|
26
|
+
* example `@/components/odoro`.
|
|
27
27
|
*/
|
|
28
28
|
import: string().check(_minLength(1)),
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
30
|
+
* Matching directory on disk, relative to the project root. For example
|
|
31
|
+
* `src/components/odoro`.
|
|
32
32
|
*/
|
|
33
33
|
directory: string().check(_minLength(1))
|
|
34
34
|
});
|
|
35
35
|
var projectSchema = object({
|
|
36
|
-
/** Version
|
|
36
|
+
/** Version of the format, so that the CLI knows whether it can read it. */
|
|
37
37
|
version: _default(literal(1), 1),
|
|
38
|
-
/**
|
|
38
|
+
/** Address of the registry: a URL, or a local path to develop it. */
|
|
39
39
|
registry: string().check(_minLength(1)),
|
|
40
|
-
/**
|
|
40
|
+
/** Where to write, and under which prefix to import. */
|
|
41
41
|
aliases: aliasesSchema,
|
|
42
|
-
/**
|
|
42
|
+
/** What was installed, indexed by registry identifier. */
|
|
43
43
|
installed: _default(record(string(), installedSchema), {})
|
|
44
44
|
});
|
|
45
45
|
function fingerprint(content) {
|
|
@@ -62,15 +62,15 @@ async function loadProject(root) {
|
|
|
62
62
|
} catch (error2) {
|
|
63
63
|
return {
|
|
64
64
|
ok: false,
|
|
65
|
-
reason: "
|
|
66
|
-
problems: [`JSON
|
|
65
|
+
reason: "invalid",
|
|
66
|
+
problems: [`Unreadable JSON \u2014 ${error2.message}`]
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
const parsed = safeParse(projectSchema, value);
|
|
70
70
|
if (!parsed.success) {
|
|
71
71
|
return {
|
|
72
72
|
ok: false,
|
|
73
|
-
reason: "
|
|
73
|
+
reason: "invalid",
|
|
74
74
|
problems: parsed.error.issues.map(
|
|
75
75
|
(issue) => `${issue.path.join(".") || CONFIG_FILE} : ${issue.message}`
|
|
76
76
|
)
|
|
@@ -89,28 +89,28 @@ async function saveProject(root, config) {
|
|
|
89
89
|
|
|
90
90
|
// src/add/rewrite.ts
|
|
91
91
|
var REGISTRY_TOKEN = "@registre";
|
|
92
|
-
function
|
|
92
|
+
function isAlias(prefix) {
|
|
93
93
|
return /^[@~#]/.test(prefix);
|
|
94
94
|
}
|
|
95
|
-
function
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
let
|
|
99
|
-
while (
|
|
100
|
-
|
|
95
|
+
function relativeImport(from, to) {
|
|
96
|
+
const fromSegments = from.split("/").slice(0, -1);
|
|
97
|
+
const toSegments = to.split("/");
|
|
98
|
+
let common = 0;
|
|
99
|
+
while (common < fromSegments.length && common < toSegments.length - 1 && fromSegments[common] === toSegments[common]) {
|
|
100
|
+
common += 1;
|
|
101
101
|
}
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
return
|
|
102
|
+
const climbs = fromSegments.length - common;
|
|
103
|
+
const descent = toSegments.slice(common).join("/");
|
|
104
|
+
return climbs === 0 ? `./${descent}` : `${"../".repeat(climbs)}${descent}`;
|
|
105
105
|
}
|
|
106
106
|
function rewriteImports(source, importPrefix, target) {
|
|
107
107
|
const prefix = importPrefix.replace(/\/$/, "");
|
|
108
|
-
if (
|
|
108
|
+
if (isAlias(prefix) || target === void 0) {
|
|
109
109
|
return source.split(`${REGISTRY_TOKEN}/`).join(`${prefix}/`);
|
|
110
110
|
}
|
|
111
111
|
return source.replaceAll(
|
|
112
112
|
new RegExp(`${REGISTRY_TOKEN}/([^'"\\s]+)`, "g"),
|
|
113
|
-
(
|
|
113
|
+
(_whole, path) => relativeImport(target, path)
|
|
114
114
|
);
|
|
115
115
|
}
|
|
116
116
|
function usedTokens(source) {
|
|
@@ -125,22 +125,22 @@ function usedTokens(source) {
|
|
|
125
125
|
|
|
126
126
|
// src/add/inspect.ts
|
|
127
127
|
var STATE_LABEL = {
|
|
128
|
-
"
|
|
129
|
-
|
|
130
|
-
"
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
128
|
+
"up-to-date": "up to date",
|
|
129
|
+
edited: "edited locally",
|
|
130
|
+
"update-available": "an update exists",
|
|
131
|
+
diverged: "edited locally, and the registry has changed",
|
|
132
|
+
missing: "missing from the project",
|
|
133
|
+
unknown: "unknown to the registry"
|
|
134
134
|
};
|
|
135
135
|
function verdict(local, deliveredHash, upstream) {
|
|
136
|
-
if (local === null) return "
|
|
137
|
-
if (upstream === null) return "
|
|
136
|
+
if (local === null) return "missing";
|
|
137
|
+
if (upstream === null) return "unknown";
|
|
138
138
|
const localChanged = fingerprint(local) !== deliveredHash;
|
|
139
139
|
const upstreamChanged = fingerprint(upstream) !== deliveredHash;
|
|
140
|
-
if (!localChanged && !upstreamChanged) return "
|
|
141
|
-
if (localChanged && !upstreamChanged) return "
|
|
142
|
-
if (!localChanged && upstreamChanged) return "
|
|
143
|
-
return "
|
|
140
|
+
if (!localChanged && !upstreamChanged) return "up-to-date";
|
|
141
|
+
if (localChanged && !upstreamChanged) return "edited";
|
|
142
|
+
if (!localChanged && upstreamChanged) return "update-available";
|
|
143
|
+
return "diverged";
|
|
144
144
|
}
|
|
145
145
|
async function inspectEntry(root, config, id, upstream) {
|
|
146
146
|
const installed = config.installed[id];
|
|
@@ -207,16 +207,16 @@ async function readDocument(location, relativePath) {
|
|
|
207
207
|
} catch (error2) {
|
|
208
208
|
return {
|
|
209
209
|
ok: false,
|
|
210
|
-
problems: [`${url} :
|
|
210
|
+
problems: [`${url} : unreachable \u2014 ${error2.message}`]
|
|
211
211
|
};
|
|
212
212
|
}
|
|
213
213
|
if (!response.ok) {
|
|
214
|
-
return { ok: false, problems: [`${url} :
|
|
214
|
+
return { ok: false, problems: [`${url} : response ${String(response.status)}.`] };
|
|
215
215
|
}
|
|
216
216
|
try {
|
|
217
217
|
return { ok: true, value: await response.json() };
|
|
218
218
|
} catch {
|
|
219
|
-
return { ok: false, problems: [`${url} :
|
|
219
|
+
return { ok: false, problems: [`${url} : the response is not JSON.`] };
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
222
|
const file = join(location, relativePath);
|
|
@@ -224,14 +224,14 @@ async function readDocument(location, relativePath) {
|
|
|
224
224
|
try {
|
|
225
225
|
raw = await readFile(file, "utf8");
|
|
226
226
|
} catch {
|
|
227
|
-
return { ok: false, problems: [`${file} :
|
|
227
|
+
return { ok: false, problems: [`${file} : not found.`] };
|
|
228
228
|
}
|
|
229
229
|
try {
|
|
230
230
|
return { ok: true, value: JSON.parse(raw) };
|
|
231
231
|
} catch (error2) {
|
|
232
232
|
return {
|
|
233
233
|
ok: false,
|
|
234
|
-
problems: [`${file} : JSON
|
|
234
|
+
problems: [`${file} : unreadable JSON \u2014 ${error2.message}`]
|
|
235
235
|
};
|
|
236
236
|
}
|
|
237
237
|
}
|
|
@@ -241,14 +241,14 @@ function asIndex(value, origin) {
|
|
|
241
241
|
return {
|
|
242
242
|
ok: false,
|
|
243
243
|
problems: [
|
|
244
|
-
`${origin} : version
|
|
244
|
+
`${origin} : format version ${String(candidate.version)} not recognised. Update the CLI.`
|
|
245
245
|
]
|
|
246
246
|
};
|
|
247
247
|
}
|
|
248
248
|
if (!Array.isArray(candidate.entries)) {
|
|
249
249
|
return {
|
|
250
250
|
ok: false,
|
|
251
|
-
problems: [`${origin} :
|
|
251
|
+
problems: [`${origin} : the index holds no list of entries.`]
|
|
252
252
|
};
|
|
253
253
|
}
|
|
254
254
|
return { ok: true, value: candidate };
|
|
@@ -258,13 +258,15 @@ function asEntry(value, id) {
|
|
|
258
258
|
if (!parsed.ok) return { ok: false, problems: parsed.problems };
|
|
259
259
|
const sources = value.sources;
|
|
260
260
|
if (typeof sources !== "object" || sources === null) {
|
|
261
|
-
return { ok: false, problems: [`${id} :
|
|
261
|
+
return { ok: false, problems: [`${id} : the entry carries no source code.`] };
|
|
262
262
|
}
|
|
263
263
|
const record2 = sources;
|
|
264
264
|
const problems = [];
|
|
265
265
|
for (const file of parsed.meta.files) {
|
|
266
266
|
if (typeof record2[file.path] !== "string") {
|
|
267
|
-
problems.push(
|
|
267
|
+
problems.push(
|
|
268
|
+
`${id} : the announced file "${file.path}" is missing from the response.`
|
|
269
|
+
);
|
|
268
270
|
}
|
|
269
271
|
}
|
|
270
272
|
if (problems.length > 0) return { ok: false, problems };
|
|
@@ -299,20 +301,20 @@ async function planWrite(root, path, content, owner) {
|
|
|
299
301
|
} catch {
|
|
300
302
|
existing = null;
|
|
301
303
|
}
|
|
302
|
-
const action = existing === null ? "
|
|
304
|
+
const action = existing === null ? "create" : existing.replaceAll("\r\n", "\n") === content.replaceAll("\r\n", "\n") ? "unchanged" : "replace";
|
|
303
305
|
return { path, content, action, owner };
|
|
304
306
|
}
|
|
305
|
-
var PENDING = ".odoro-
|
|
307
|
+
var PENDING = ".odoro-pending";
|
|
306
308
|
async function applyPlan(root, plan) {
|
|
307
|
-
const todo = plan.filter((entry) => entry.action !== "
|
|
308
|
-
const skipped = plan.filter((entry) => entry.action === "
|
|
309
|
+
const todo = plan.filter((entry) => entry.action !== "unchanged");
|
|
310
|
+
const skipped = plan.filter((entry) => entry.action === "unchanged").map((entry) => entry.path);
|
|
309
311
|
const pending = [];
|
|
310
312
|
const previous = /* @__PURE__ */ new Map();
|
|
311
313
|
try {
|
|
312
314
|
for (const entry of todo) {
|
|
313
315
|
const target = join(root, entry.path);
|
|
314
316
|
await mkdir(dirname(target), { recursive: true });
|
|
315
|
-
if (entry.action === "
|
|
317
|
+
if (entry.action === "replace") {
|
|
316
318
|
previous.set(entry.path, await readFile(target, "utf8"));
|
|
317
319
|
} else {
|
|
318
320
|
previous.set(entry.path, null);
|
|
@@ -381,14 +383,12 @@ async function prepareInstall(registry, requested) {
|
|
|
381
383
|
continue;
|
|
382
384
|
}
|
|
383
385
|
if (matches.length > 1) {
|
|
384
|
-
problems.push(
|
|
385
|
-
`"${asked}" est ambigu : ${matches.join(", ")}. Precisez la categorie.`
|
|
386
|
-
);
|
|
386
|
+
problems.push(`"${asked}" is ambiguous: ${matches.join(", ")}. Name the category.`);
|
|
387
387
|
continue;
|
|
388
388
|
}
|
|
389
389
|
const near = suggest(asked, known);
|
|
390
390
|
problems.push(
|
|
391
|
-
near.length > 0 ? `"${asked}"
|
|
391
|
+
near.length > 0 ? `"${asked}" was not found. Did you mean ${near.join(", ")}?` : `"${asked}" was not found. "odoro list" gives the catalogue.`
|
|
392
392
|
);
|
|
393
393
|
}
|
|
394
394
|
if (problems.length > 0) return { ok: false, problems };
|
|
@@ -442,9 +442,9 @@ function recordInstall(previous, entries, plan, now) {
|
|
|
442
442
|
|
|
443
443
|
// src/add/weight.ts
|
|
444
444
|
var BACKEND_WEIGHT = {
|
|
445
|
-
/**
|
|
445
|
+
/** Minimal 3D scene: geometry, standard material, directional light. */
|
|
446
446
|
three: 130,
|
|
447
|
-
/**
|
|
447
|
+
/** Full-screen render: context, program, triangle. */
|
|
448
448
|
ogl: 13
|
|
449
449
|
};
|
|
450
450
|
function weighEntries(entries) {
|
|
@@ -459,12 +459,12 @@ function weighEntries(entries) {
|
|
|
459
459
|
const warnings = [];
|
|
460
460
|
for (const [backend, ids] of byBackend) {
|
|
461
461
|
const kilobytes = BACKEND_WEIGHT[backend];
|
|
462
|
-
const which = ids.length === 1 ? ids[0] : `${String(ids.length)}
|
|
462
|
+
const which = ids.length === 1 ? ids[0] : `${String(ids.length)} components`;
|
|
463
463
|
warnings.push({
|
|
464
464
|
backend,
|
|
465
465
|
kilobytes,
|
|
466
466
|
entries: ids,
|
|
467
|
-
message: backend === "three" ? `${String(which)}
|
|
467
|
+
message: backend === "three" ? `${String(which)} loads a 3D scene: about ${String(kilobytes)} kB compressed on the first paint. The light backend asks for ${String(BACKEND_WEIGHT.ogl)}, when a full-screen effect is enough.` : `${String(which)} loads the light backend: about ${String(kilobytes)} kB compressed.`
|
|
468
468
|
});
|
|
469
469
|
}
|
|
470
470
|
return warnings.sort((a, b) => b.kilobytes - a.kilobytes);
|
|
@@ -486,9 +486,7 @@ function canAsk(options) {
|
|
|
486
486
|
return options.yes !== true && process.stdin.isTTY === true;
|
|
487
487
|
}
|
|
488
488
|
function needsConfirmation(what) {
|
|
489
|
-
error(
|
|
490
|
-
`${what} Relancez avec ${colors.cyan("--yes")} pour l'accepter sans question.`
|
|
491
|
-
);
|
|
489
|
+
error(`${what} Run again with ${colors.cyan("--yes")} to accept it without asking.`);
|
|
492
490
|
return 1;
|
|
493
491
|
}
|
|
494
492
|
function fail(title, problems) {
|
|
@@ -500,24 +498,24 @@ async function requireProject(root) {
|
|
|
500
498
|
const loaded = await loadProject(root);
|
|
501
499
|
if (loaded.ok) return loaded.config;
|
|
502
500
|
if (loaded.reason === "absent") {
|
|
503
|
-
error(`
|
|
501
|
+
error(`No ${CONFIG_FILE} here. Run ${colors.cyan("odoro init")} first.`);
|
|
504
502
|
return 1;
|
|
505
503
|
}
|
|
506
|
-
return fail(`${CONFIG_FILE}
|
|
504
|
+
return fail(`${CONFIG_FILE} is unreadable:`, loaded.problems);
|
|
507
505
|
}
|
|
508
506
|
async function initCommand(options) {
|
|
509
507
|
const { root } = options;
|
|
510
508
|
const existing = await loadProject(root);
|
|
511
509
|
if (existing.ok && options.yes !== true) {
|
|
512
510
|
if (!canAsk(options)) {
|
|
513
|
-
return needsConfirmation(`${CONFIG_FILE}
|
|
511
|
+
return needsConfirmation(`${CONFIG_FILE} already exists and would be replaced.`);
|
|
514
512
|
}
|
|
515
513
|
const replace = await prompts.confirm({
|
|
516
|
-
message: `${CONFIG_FILE}
|
|
514
|
+
message: `${CONFIG_FILE} already exists. Replace it?`,
|
|
517
515
|
initialValue: false
|
|
518
516
|
});
|
|
519
517
|
if (prompts.isCancel(replace) || !replace) {
|
|
520
|
-
info("
|
|
518
|
+
info("Nothing was changed.");
|
|
521
519
|
return 0;
|
|
522
520
|
}
|
|
523
521
|
}
|
|
@@ -525,71 +523,69 @@ async function initCommand(options) {
|
|
|
525
523
|
const suggested = defaultAliases(guess);
|
|
526
524
|
if (guess === null) {
|
|
527
525
|
warn(
|
|
528
|
-
`
|
|
526
|
+
`No alias found in tsconfig.json: the components will go to ${colors.cyan(suggested.directory)}/ and will import each other relatively.`
|
|
529
527
|
);
|
|
530
528
|
} else {
|
|
531
529
|
info(
|
|
532
|
-
`Alias
|
|
530
|
+
`Alias found in tsconfig.json: ${colors.cyan(`${guess.prefix}/*`)} towards ${colors.cyan(`${guess.directory}/`)}.`
|
|
533
531
|
);
|
|
534
532
|
}
|
|
535
533
|
let aliases = suggested;
|
|
536
534
|
let registry = options.registry ?? DEFAULT_REGISTRY;
|
|
537
535
|
if (canAsk(options)) {
|
|
538
536
|
const directory = await prompts.text({
|
|
539
|
-
message: "
|
|
537
|
+
message: "Where should the components be written?",
|
|
540
538
|
initialValue: suggested.directory
|
|
541
539
|
});
|
|
542
540
|
if (prompts.isCancel(directory)) return 0;
|
|
543
541
|
const importPrefix = await prompts.text({
|
|
544
|
-
message: "
|
|
542
|
+
message: "Under which prefix should they be imported?",
|
|
545
543
|
initialValue: suggested.import
|
|
546
544
|
});
|
|
547
545
|
if (prompts.isCancel(importPrefix)) return 0;
|
|
548
|
-
const source = await prompts.text({ message: "
|
|
546
|
+
const source = await prompts.text({ message: "Registry?", initialValue: registry });
|
|
549
547
|
if (prompts.isCancel(source)) return 0;
|
|
550
548
|
aliases = { directory, import: importPrefix };
|
|
551
549
|
registry = source;
|
|
552
550
|
}
|
|
553
551
|
const config = { version: 1, registry, aliases, installed: {} };
|
|
554
552
|
await saveProject(root, config);
|
|
555
|
-
success(`${CONFIG_FILE}
|
|
553
|
+
success(`${CONFIG_FILE} written.`);
|
|
556
554
|
console.log(` ${colors.dim("destination")} ${aliases.directory}/`);
|
|
557
555
|
console.log(` ${colors.dim("imports")} ${aliases.import}/\u2026`);
|
|
558
|
-
console.log(` ${colors.dim("
|
|
556
|
+
console.log(` ${colors.dim("registry")} ${registry}`);
|
|
559
557
|
console.log(`
|
|
560
|
-
${colors.cyan("odoro list")}
|
|
558
|
+
${colors.cyan("odoro list")} gives the catalogue.
|
|
561
559
|
`);
|
|
562
560
|
return 0;
|
|
563
561
|
}
|
|
564
562
|
var ACTION_MARK = {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
563
|
+
create: colors.green("+"),
|
|
564
|
+
replace: colors.yellow("~"),
|
|
565
|
+
unchanged: colors.dim("=")
|
|
568
566
|
};
|
|
569
567
|
async function addCommand(options, requested) {
|
|
570
568
|
const { root } = options;
|
|
571
569
|
const config = await requireProject(root);
|
|
572
570
|
if (typeof config === "number") return config;
|
|
573
571
|
if (requested.length === 0) {
|
|
574
|
-
error('
|
|
572
|
+
error('Nothing to install. Give a name, or "odoro list" for the catalogue.');
|
|
575
573
|
return 1;
|
|
576
574
|
}
|
|
577
575
|
const registry = openRegistry(options.registry ?? config.registry, root);
|
|
578
576
|
const prepared = await prepareInstall(registry, requested);
|
|
579
|
-
if (!prepared.ok) return fail("
|
|
577
|
+
if (!prepared.ok) return fail("Install impossible:", prepared.problems);
|
|
580
578
|
const { entries, implied } = prepared;
|
|
581
579
|
if (implied.length > 0) {
|
|
582
|
-
info(`
|
|
580
|
+
info(`Dependencies added: ${implied.map((id) => colors.cyan(id)).join(", ")}`);
|
|
583
581
|
}
|
|
584
582
|
for (const warning of weighEntries(entries)) warn(warning.message);
|
|
585
583
|
const missing = await missingPackages(root, requiredPackages(entries));
|
|
586
584
|
if (missing.length > 0) {
|
|
587
|
-
warn(
|
|
588
|
-
`A installer ensuite : ${missing.map((name) => colors.cyan(name)).join(" ")}`
|
|
589
|
-
);
|
|
585
|
+
warn(`To install next: ${missing.map((name) => colors.cyan(name)).join(" ")}`);
|
|
590
586
|
}
|
|
591
587
|
const plan = await planInstall(root, config, entries);
|
|
592
|
-
const replacing = plan.filter((write) => write.action === "
|
|
588
|
+
const replacing = plan.filter((write) => write.action === "replace");
|
|
593
589
|
console.log("");
|
|
594
590
|
for (const write of plan) {
|
|
595
591
|
console.log(` ${ACTION_MARK[write.action]} ${write.path}`);
|
|
@@ -598,15 +594,15 @@ async function addCommand(options, requested) {
|
|
|
598
594
|
if (replacing.length > 0 && options.yes !== true) {
|
|
599
595
|
if (!canAsk(options)) {
|
|
600
596
|
return needsConfirmation(
|
|
601
|
-
`${String(replacing.length)}
|
|
597
|
+
`${String(replacing.length)} existing file(s) would be replaced.`
|
|
602
598
|
);
|
|
603
599
|
}
|
|
604
600
|
const go = await prompts.confirm({
|
|
605
|
-
message: `${String(replacing.length)}
|
|
601
|
+
message: `${String(replacing.length)} existing file(s) will be replaced. Continue?`,
|
|
606
602
|
initialValue: false
|
|
607
603
|
});
|
|
608
604
|
if (prompts.isCancel(go) || !go) {
|
|
609
|
-
info("
|
|
605
|
+
info("Nothing was written.");
|
|
610
606
|
return 0;
|
|
611
607
|
}
|
|
612
608
|
}
|
|
@@ -618,10 +614,10 @@ async function addCommand(options, requested) {
|
|
|
618
614
|
};
|
|
619
615
|
await saveProject(root, updated);
|
|
620
616
|
const written = String(report.written.length);
|
|
621
|
-
const skipped = report.skipped.length > 0 ? `, ${String(report.skipped.length)}
|
|
622
|
-
success(`${written}
|
|
617
|
+
const skipped = report.skipped.length > 0 ? `, ${String(report.skipped.length)} unchanged` : "";
|
|
618
|
+
success(`${written} file(s) written${skipped}.`);
|
|
623
619
|
} catch (cause) {
|
|
624
|
-
error("
|
|
620
|
+
error("Write interrupted: the project is unchanged.", cause);
|
|
625
621
|
return 1;
|
|
626
622
|
}
|
|
627
623
|
warnUndeclared(entries);
|
|
@@ -637,7 +633,7 @@ function warnUndeclared(entries) {
|
|
|
637
633
|
const matched = [...targets].some((target) => target.startsWith(token));
|
|
638
634
|
if (!matched) {
|
|
639
635
|
warn(
|
|
640
|
-
`${entry.id}
|
|
636
|
+
`${entry.id} imports ${colors.cyan(token)} without declaring it: report it to the registry.`
|
|
641
637
|
);
|
|
642
638
|
}
|
|
643
639
|
}
|
|
@@ -667,7 +663,7 @@ async function listCommand(options) {
|
|
|
667
663
|
const installed = loaded.ok ? new Set(Object.keys(loaded.config.installed)) : /* @__PURE__ */ new Set();
|
|
668
664
|
const registry = openRegistry(location, root);
|
|
669
665
|
const index = await registry.index();
|
|
670
|
-
if (!index.ok) return fail("Catalogue
|
|
666
|
+
if (!index.ok) return fail("Catalogue unreadable:", index.problems);
|
|
671
667
|
const byCategory = /* @__PURE__ */ new Map();
|
|
672
668
|
for (const entry of index.value.entries) {
|
|
673
669
|
byCategory.set(entry.category, [...byCategory.get(entry.category) ?? [], entry]);
|
|
@@ -689,16 +685,16 @@ async function listCommand(options) {
|
|
|
689
685
|
console.log("");
|
|
690
686
|
}
|
|
691
687
|
const total = String(index.value.entries.length);
|
|
692
|
-
const marked = installed.size > 0 ? `, ${colors.green("\u2713")} =
|
|
693
|
-
console.log(` ${colors.dim(`${total}
|
|
688
|
+
const marked = installed.size > 0 ? `, ${colors.green("\u2713")} = already installed` : "";
|
|
689
|
+
console.log(` ${colors.dim(`${total} entr${total === "1" ? "y" : "ies"}${marked}`)}
|
|
694
690
|
`);
|
|
695
691
|
return 0;
|
|
696
692
|
}
|
|
697
693
|
function stateColor(state) {
|
|
698
694
|
const label = STATE_LABEL[state];
|
|
699
|
-
if (state === "
|
|
700
|
-
if (state === "
|
|
701
|
-
if (state === "
|
|
695
|
+
if (state === "up-to-date") return colors.green(label);
|
|
696
|
+
if (state === "edited") return colors.cyan(label);
|
|
697
|
+
if (state === "missing" || state === "diverged") return colors.red(label);
|
|
702
698
|
return colors.yellow(label);
|
|
703
699
|
}
|
|
704
700
|
async function diffCommand(options) {
|
|
@@ -707,7 +703,7 @@ async function diffCommand(options) {
|
|
|
707
703
|
if (typeof config === "number") return config;
|
|
708
704
|
const ids = Object.keys(config.installed);
|
|
709
705
|
if (ids.length === 0) {
|
|
710
|
-
info("
|
|
706
|
+
info("No component installed.");
|
|
711
707
|
return 0;
|
|
712
708
|
}
|
|
713
709
|
const registry = openRegistry(options.registry ?? config.registry, root);
|
|
@@ -716,16 +712,18 @@ async function diffCommand(options) {
|
|
|
716
712
|
console.log("");
|
|
717
713
|
for (const report of reports) {
|
|
718
714
|
if (report.orphan) {
|
|
719
|
-
console.log(
|
|
715
|
+
console.log(
|
|
716
|
+
` ${colors.cyan(report.id)} ${colors.dim("\u2014 missing from the registry")}`
|
|
717
|
+
);
|
|
720
718
|
interesting += 1;
|
|
721
719
|
continue;
|
|
722
720
|
}
|
|
723
721
|
for (const file of report.files) {
|
|
724
|
-
if (file.state === "
|
|
722
|
+
if (file.state === "up-to-date") continue;
|
|
725
723
|
interesting += 1;
|
|
726
724
|
console.log(` ${colors.cyan(report.id)} ${colors.dim(file.path)}`);
|
|
727
725
|
console.log(` ${stateColor(file.state)}`);
|
|
728
|
-
if (file.local !== null && file.upstream !== null && file.state !== "
|
|
726
|
+
if (file.local !== null && file.upstream !== null && file.state !== "edited") {
|
|
729
727
|
const changes = previewChanges(file.local, file.upstream);
|
|
730
728
|
for (const line of changes.removed)
|
|
731
729
|
console.log(` ${colors.red(`- ${line.trim()}`)}`);
|
|
@@ -736,11 +734,13 @@ async function diffCommand(options) {
|
|
|
736
734
|
}
|
|
737
735
|
}
|
|
738
736
|
if (interesting === 0) {
|
|
739
|
-
success(
|
|
737
|
+
success(
|
|
738
|
+
`${String(ids.length)} entr${ids.length === 1 ? "y" : "ies"} installed, all up to date.`
|
|
739
|
+
);
|
|
740
740
|
return 0;
|
|
741
741
|
}
|
|
742
742
|
console.log(
|
|
743
|
-
` ${colors.dim(`odoro add <
|
|
743
|
+
` ${colors.dim(`odoro add <name> rewrites an entry from the registry. Your edits would be lost.`)}
|
|
744
744
|
`
|
|
745
745
|
);
|
|
746
746
|
return 0;
|
|
@@ -751,12 +751,12 @@ async function doctorCommand(options) {
|
|
|
751
751
|
const notes = [];
|
|
752
752
|
const loaded = await loadProject(root);
|
|
753
753
|
if (!loaded.ok) {
|
|
754
|
-
return loaded.reason === "absent" ? fail("
|
|
754
|
+
return loaded.reason === "absent" ? fail("Diagnostics impossible:", [`no ${CONFIG_FILE}. Run "odoro init".`]) : fail(`${CONFIG_FILE} is unreadable:`, loaded.problems);
|
|
755
755
|
}
|
|
756
756
|
const config = loaded.config;
|
|
757
|
-
notes.push(`
|
|
757
|
+
notes.push(`registry: ${config.registry}`);
|
|
758
758
|
if (!isRemote(config.registry)) {
|
|
759
|
-
notes.push("
|
|
759
|
+
notes.push("local registry: the other machines of the team will not see it.");
|
|
760
760
|
}
|
|
761
761
|
const { access } = await import('fs/promises');
|
|
762
762
|
try {
|
|
@@ -764,49 +764,49 @@ async function doctorCommand(options) {
|
|
|
764
764
|
} catch {
|
|
765
765
|
if (Object.keys(config.installed).length > 0) {
|
|
766
766
|
troubles.push(
|
|
767
|
-
`${config.aliases.directory}/
|
|
767
|
+
`${config.aliases.directory}/ does not exist, while entries are recorded in it.`
|
|
768
768
|
);
|
|
769
769
|
}
|
|
770
770
|
}
|
|
771
771
|
const registry = openRegistry(options.registry ?? config.registry, root);
|
|
772
772
|
const reports = await inspectAll(root, config, registry);
|
|
773
|
-
let
|
|
773
|
+
let edited = 0;
|
|
774
774
|
for (const report of reports) {
|
|
775
775
|
if (report.orphan) {
|
|
776
|
-
notes.push(`${report.id}
|
|
776
|
+
notes.push(`${report.id} is no longer served by the registry.`);
|
|
777
777
|
continue;
|
|
778
778
|
}
|
|
779
779
|
for (const file of report.files) {
|
|
780
|
-
if (file.state === "
|
|
781
|
-
troubles.push(`${report.id}
|
|
780
|
+
if (file.state === "missing") {
|
|
781
|
+
troubles.push(`${report.id}: ${file.path} is recorded as installed but missing.`);
|
|
782
782
|
}
|
|
783
|
-
if (file.state === "
|
|
784
|
-
if (file.state === "
|
|
785
|
-
notes.push(`${report.id}
|
|
783
|
+
if (file.state === "edited") edited += 1;
|
|
784
|
+
if (file.state === "update-available" || file.state === "diverged") {
|
|
785
|
+
notes.push(`${report.id}: ${STATE_LABEL[file.state]} (${file.path}).`);
|
|
786
786
|
}
|
|
787
787
|
}
|
|
788
788
|
}
|
|
789
|
-
if (
|
|
789
|
+
if (edited > 0) {
|
|
790
790
|
notes.push(
|
|
791
|
-
`${String(
|
|
791
|
+
`${String(edited)} file(s) edited locally: "odoro add" would rewrite them.`
|
|
792
792
|
);
|
|
793
793
|
}
|
|
794
794
|
const served = reports.map((report) => report.upstream).filter((entry) => entry !== null);
|
|
795
795
|
for (const name of await missingPackages(root, requiredPackages(served))) {
|
|
796
796
|
troubles.push(
|
|
797
|
-
`${name}
|
|
797
|
+
`${name} is required by an installed component but missing from the package.json.`
|
|
798
798
|
);
|
|
799
799
|
}
|
|
800
800
|
console.log("");
|
|
801
801
|
for (const note of notes) console.log(` ${colors.dim("\xB7")} ${note}`);
|
|
802
802
|
if (notes.length > 0) console.log("");
|
|
803
803
|
if (troubles.length === 0) {
|
|
804
|
-
success("
|
|
804
|
+
success("Nothing to report.");
|
|
805
805
|
return 0;
|
|
806
806
|
}
|
|
807
807
|
for (const trouble of troubles) console.error(` ${colors.red("\xB7")} ${trouble}`);
|
|
808
808
|
console.log("");
|
|
809
|
-
error(`${String(troubles.length)}
|
|
809
|
+
error(`${String(troubles.length)} problem(s).`);
|
|
810
810
|
return 1;
|
|
811
811
|
}
|
|
812
812
|
|