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,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, loadSdk, ensureToken, DEFAULT_API_URL, cancelled, chooseEnvironment, chooseRegion, provision } from './chunk-LIKVHWB4.js';
|
|
3
3
|
import { execSync, spawn } from 'child_process';
|
|
4
4
|
import { existsSync, statSync, readdirSync, readFileSync } from 'fs';
|
|
5
5
|
import { resolve, basename, dirname, join } from 'path';
|
|
@@ -14,86 +14,86 @@ var MODULES = [
|
|
|
14
14
|
id: "libs",
|
|
15
15
|
label: "Libraries",
|
|
16
16
|
hint: "styles, tokens, UI and motion",
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
ticked: true,
|
|
18
|
+
packageName: "@odoro-cli/libs"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
id: "router",
|
|
22
22
|
label: "Router",
|
|
23
23
|
hint: "ships with the libraries \u2014 wires up the pages",
|
|
24
|
-
|
|
24
|
+
ticked: true
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
id: "icons",
|
|
28
28
|
label: "Icons",
|
|
29
29
|
hint: "five families, imported one by one",
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
ticked: true,
|
|
31
|
+
packageName: "@odoro-cli/icons"
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
id: "engine",
|
|
35
35
|
label: "Engine",
|
|
36
36
|
hint: "WebGL, surfaces and motion policy",
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
ticked: false,
|
|
38
|
+
packageName: "@odoro-cli/engine"
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
id: "registre",
|
|
42
42
|
label: "Component registry",
|
|
43
43
|
hint: "copied by `odoro add` \u2014 pulls in the engine",
|
|
44
|
-
|
|
44
|
+
ticked: false
|
|
45
45
|
}
|
|
46
46
|
];
|
|
47
|
-
var MODULE_IDS = MODULES.map((
|
|
48
|
-
var
|
|
49
|
-
(
|
|
50
|
-
).map((
|
|
51
|
-
function
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
47
|
+
var MODULE_IDS = MODULES.map((module) => module.id);
|
|
48
|
+
var DEFAULT_MODULES = MODULES.filter(
|
|
49
|
+
(module) => module.ticked
|
|
50
|
+
).map((module) => module.id);
|
|
51
|
+
function resolveModules(selection) {
|
|
52
|
+
const chosen = new Set(selection);
|
|
53
|
+
const warnings = [];
|
|
54
|
+
if (chosen.has("router") && !chosen.has("libs")) {
|
|
55
|
+
chosen.delete("router");
|
|
56
|
+
warnings.push(
|
|
57
57
|
"The router lives in the libraries (@odoro-cli/libs/router) \u2014 without them it is dropped."
|
|
58
58
|
);
|
|
59
59
|
}
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
if (chosen.has("registre") && !chosen.has("engine")) {
|
|
61
|
+
chosen.add("engine");
|
|
62
|
+
warnings.push(
|
|
63
63
|
"Almost every registry entry imports @odoro-cli/engine, so the engine was added."
|
|
64
64
|
);
|
|
65
65
|
}
|
|
66
|
-
return { modules: MODULE_IDS.filter((id) =>
|
|
66
|
+
return { modules: MODULE_IDS.filter((id) => chosen.has(id)), warnings };
|
|
67
67
|
}
|
|
68
|
-
function
|
|
69
|
-
const
|
|
70
|
-
return MODULES.filter(
|
|
71
|
-
(
|
|
72
|
-
);
|
|
68
|
+
function packagesFor(modules) {
|
|
69
|
+
const chosen = new Set(modules);
|
|
70
|
+
return MODULES.filter(
|
|
71
|
+
(module) => module.packageName !== void 0 && chosen.has(module.id)
|
|
72
|
+
).map((module) => module.packageName);
|
|
73
73
|
}
|
|
74
|
-
function
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
if (!
|
|
78
|
-
else if (!
|
|
79
|
-
if (
|
|
80
|
-
return
|
|
74
|
+
function variantsFor(modules) {
|
|
75
|
+
const chosen = new Set(modules);
|
|
76
|
+
const variants = [];
|
|
77
|
+
if (!chosen.has("libs")) variants.push("without-libs");
|
|
78
|
+
else if (!chosen.has("router")) variants.push("without-router");
|
|
79
|
+
if (chosen.has("engine")) variants.push("with-engine");
|
|
80
|
+
return variants;
|
|
81
81
|
}
|
|
82
|
-
function
|
|
82
|
+
function keepsRoutes(modules) {
|
|
83
83
|
return modules.includes("router");
|
|
84
84
|
}
|
|
85
|
-
function
|
|
86
|
-
const
|
|
87
|
-
if (
|
|
85
|
+
function readModules(value) {
|
|
86
|
+
const raw = value.split(",").map((part) => part.trim()).filter((part) => part !== "");
|
|
87
|
+
if (raw.length === 1 && (raw[0] === "none" || raw[0] === "aucun")) {
|
|
88
88
|
return { modules: [] };
|
|
89
89
|
}
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
90
|
+
const unknown = raw.find((part) => !MODULE_IDS.includes(part));
|
|
91
|
+
if (unknown !== void 0) {
|
|
92
92
|
return {
|
|
93
|
-
|
|
93
|
+
error: `Unknown module: "${unknown}". Available: ${MODULE_IDS.join(", ")}, or "none".`
|
|
94
94
|
};
|
|
95
95
|
}
|
|
96
|
-
return { modules:
|
|
96
|
+
return { modules: raw };
|
|
97
97
|
}
|
|
98
98
|
var PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"];
|
|
99
99
|
function detectPackageManager(userAgent = process.env["npm_config_user_agent"]) {
|
|
@@ -111,18 +111,18 @@ function toPackageName(input) {
|
|
|
111
111
|
return input.trim().toLowerCase().replace(/^[._]+/, "").replace(/[^a-z0-9\-~]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 214) || "odoro-app";
|
|
112
112
|
}
|
|
113
113
|
function validatePackageName(name) {
|
|
114
|
-
if (name.trim() === "") return "
|
|
115
|
-
if (name.length > 214) return "
|
|
116
|
-
if (/^[._]/.test(name)) return '
|
|
114
|
+
if (name.trim() === "") return "The project name cannot be empty.";
|
|
115
|
+
if (name.length > 214) return "The project name cannot exceed 214 characters.";
|
|
116
|
+
if (/^[._]/.test(name)) return 'The project name cannot start with "." or "_".';
|
|
117
117
|
if (!/^[a-z0-9\-~][a-z0-9\-._~]*$/.test(name)) {
|
|
118
|
-
return "
|
|
118
|
+
return "The name must be lowercase, without spaces or special characters.";
|
|
119
119
|
}
|
|
120
120
|
return void 0;
|
|
121
121
|
}
|
|
122
122
|
function inspectTarget(directory) {
|
|
123
123
|
if (!existsSync(directory)) return "absent";
|
|
124
124
|
const entries = readdirSync(directory).filter((entry) => entry !== ".git");
|
|
125
|
-
return entries.length === 0 ? "
|
|
125
|
+
return entries.length === 0 ? "empty" : "occupied";
|
|
126
126
|
}
|
|
127
127
|
function targetFileName(name) {
|
|
128
128
|
return name.startsWith("_") ? `.${name.slice(1)}` : name;
|
|
@@ -136,29 +136,29 @@ function templatesRoot(from = fileURLToPath(import.meta.url)) {
|
|
|
136
136
|
if (parent === directory) break;
|
|
137
137
|
directory = parent;
|
|
138
138
|
}
|
|
139
|
-
throw new Error("[odoro]
|
|
139
|
+
throw new Error("[odoro] Templates directory not found from " + from);
|
|
140
140
|
}
|
|
141
141
|
function availableTemplates(root = templatesRoot()) {
|
|
142
142
|
return readdirSync(root).filter((entry) => statSync(resolve(root, entry)).isDirectory()).sort();
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
// src/scaffold/versions
|
|
146
|
-
var
|
|
147
|
-
"@odoro-cli/engine": "
|
|
148
|
-
"@odoro-cli/icons": "
|
|
149
|
-
"@odoro-cli/libs": "1.0
|
|
150
|
-
"@odoro-cli/server": "1.0
|
|
151
|
-
"create-odoro": "1.0.
|
|
152
|
-
"odoro": "1.0
|
|
145
|
+
// src/scaffold/family-versions.generated.ts
|
|
146
|
+
var FAMILY_VERSIONS = {
|
|
147
|
+
"@odoro-cli/engine": "2.0.0",
|
|
148
|
+
"@odoro-cli/icons": "2.0.0",
|
|
149
|
+
"@odoro-cli/libs": "2.1.0",
|
|
150
|
+
"@odoro-cli/server": "1.1.0",
|
|
151
|
+
"create-odoro": "1.0.11",
|
|
152
|
+
"odoro": "2.1.0"
|
|
153
153
|
};
|
|
154
154
|
|
|
155
155
|
// src/scaffold/scaffold.ts
|
|
156
|
-
var
|
|
157
|
-
var
|
|
156
|
+
var VARIANTS_DIRECTORY = "_variants";
|
|
157
|
+
var OPTIONAL_PACKAGES = ["@odoro-cli/libs", "@odoro-cli/icons", "@odoro-cli/engine"];
|
|
158
158
|
function cliVersion() {
|
|
159
159
|
try {
|
|
160
|
-
const
|
|
161
|
-
const { version } = JSON.parse(readFileSync(
|
|
160
|
+
const manifest = join(dirname(templatesRoot()), "package.json");
|
|
161
|
+
const { version } = JSON.parse(readFileSync(manifest, "utf8"));
|
|
162
162
|
return version;
|
|
163
163
|
} catch {
|
|
164
164
|
return "latest";
|
|
@@ -167,7 +167,7 @@ function cliVersion() {
|
|
|
167
167
|
async function copyDirectory(from, to, written, prefix = "") {
|
|
168
168
|
await mkdir(to, { recursive: true });
|
|
169
169
|
for (const entry of await readdir(from, { withFileTypes: true })) {
|
|
170
|
-
if (prefix === "" && entry.name ===
|
|
170
|
+
if (prefix === "" && entry.name === VARIANTS_DIRECTORY) continue;
|
|
171
171
|
const source = join(from, entry.name);
|
|
172
172
|
const name = targetFileName(entry.name);
|
|
173
173
|
const destination = join(to, name);
|
|
@@ -183,11 +183,11 @@ async function copyDirectory(from, to, written, prefix = "") {
|
|
|
183
183
|
function isOdoroPackage(name) {
|
|
184
184
|
return name === "odoro" || name.startsWith("@odoro-cli/");
|
|
185
185
|
}
|
|
186
|
-
function
|
|
187
|
-
if (
|
|
188
|
-
if (
|
|
189
|
-
const
|
|
190
|
-
return
|
|
186
|
+
function requestedVersion(name, cli) {
|
|
187
|
+
if (cli === "latest") return "latest";
|
|
188
|
+
if (name === "odoro") return `^${cli}`;
|
|
189
|
+
const collected = FAMILY_VERSIONS[name];
|
|
190
|
+
return collected === void 0 ? "latest" : `^${collected}`;
|
|
191
191
|
}
|
|
192
192
|
function alignOdoroVersions(manifest, version) {
|
|
193
193
|
const aligned = { ...manifest };
|
|
@@ -196,18 +196,18 @@ function alignOdoroVersions(manifest, version) {
|
|
|
196
196
|
if (typeof deps !== "object" || deps === null) continue;
|
|
197
197
|
const next = {};
|
|
198
198
|
for (const [name, range] of Object.entries(deps)) {
|
|
199
|
-
next[name] = isOdoroPackage(name) ?
|
|
199
|
+
next[name] = isOdoroPackage(name) ? requestedVersion(name, version) : range;
|
|
200
200
|
}
|
|
201
201
|
aligned[field] = next;
|
|
202
202
|
}
|
|
203
203
|
return aligned;
|
|
204
204
|
}
|
|
205
|
-
function
|
|
206
|
-
const
|
|
205
|
+
function adjustModules(manifest, modules) {
|
|
206
|
+
const wanted = new Set(packagesFor(modules));
|
|
207
207
|
const deps = { ...manifest["dependencies"] ?? {} };
|
|
208
|
-
for (const
|
|
209
|
-
if (
|
|
210
|
-
else delete deps[
|
|
208
|
+
for (const packageName of OPTIONAL_PACKAGES) {
|
|
209
|
+
if (wanted.has(packageName)) deps[packageName] ??= "latest";
|
|
210
|
+
else delete deps[packageName];
|
|
211
211
|
}
|
|
212
212
|
return {
|
|
213
213
|
...manifest,
|
|
@@ -216,21 +216,21 @@ function ajusterModules(manifest, modules) {
|
|
|
216
216
|
)
|
|
217
217
|
};
|
|
218
218
|
}
|
|
219
|
-
async function
|
|
220
|
-
const
|
|
221
|
-
if (!existsSync(
|
|
222
|
-
throw new Error(`[odoro]
|
|
219
|
+
async function layVariant(source, target, name) {
|
|
220
|
+
const root = join(source, VARIANTS_DIRECTORY, name);
|
|
221
|
+
if (!existsSync(root)) {
|
|
222
|
+
throw new Error(`[odoro] Template variant not found: "${name}".`);
|
|
223
223
|
}
|
|
224
|
-
const
|
|
225
|
-
await copyDirectory(
|
|
226
|
-
return
|
|
224
|
+
const laid = [];
|
|
225
|
+
await copyDirectory(root, target, laid);
|
|
226
|
+
return laid;
|
|
227
227
|
}
|
|
228
228
|
async function renamePackage(target, packageName, version, modules) {
|
|
229
229
|
const file = join(target, "package.json");
|
|
230
230
|
if (!existsSync(file)) return;
|
|
231
231
|
const manifest = JSON.parse(await readFile(file, "utf8"));
|
|
232
|
-
const
|
|
233
|
-
const renamed = alignOdoroVersions(
|
|
232
|
+
const adjusted = adjustModules({ ...manifest, name: packageName }, modules);
|
|
233
|
+
const renamed = alignOdoroVersions(adjusted, version);
|
|
234
234
|
await writeFile(file, `${JSON.stringify(renamed, null, 2)}
|
|
235
235
|
`, "utf8");
|
|
236
236
|
}
|
|
@@ -238,7 +238,7 @@ async function scaffold(options) {
|
|
|
238
238
|
const root = options.root ?? templatesRoot();
|
|
239
239
|
const source = join(root, options.template);
|
|
240
240
|
if (!existsSync(source)) {
|
|
241
|
-
throw new Error(`[odoro]
|
|
241
|
+
throw new Error(`[odoro] Unknown template: "${options.template}".`);
|
|
242
242
|
}
|
|
243
243
|
if (options.overwrite === "ecraser" && existsSync(options.target)) {
|
|
244
244
|
for (const entry of await readdir(options.target)) {
|
|
@@ -246,17 +246,17 @@ async function scaffold(options) {
|
|
|
246
246
|
await rm(join(options.target, entry), { recursive: true, force: true });
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
|
-
const modules = options.modules ??
|
|
249
|
+
const modules = options.modules ?? DEFAULT_MODULES;
|
|
250
250
|
const files = [];
|
|
251
251
|
await copyDirectory(source, options.target, files);
|
|
252
|
-
if (!
|
|
253
|
-
for (const
|
|
254
|
-
await rm(join(options.target,
|
|
252
|
+
if (!keepsRoutes(modules)) {
|
|
253
|
+
for (const file of ["src/router.tsx", "client/src/router.tsx"]) {
|
|
254
|
+
await rm(join(options.target, file), { force: true });
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
|
-
const
|
|
258
|
-
for (const
|
|
259
|
-
|
|
257
|
+
const laid = [];
|
|
258
|
+
for (const variant of variantsFor(modules)) {
|
|
259
|
+
laid.push(...await layVariant(source, options.target, variant));
|
|
260
260
|
}
|
|
261
261
|
await renamePackage(
|
|
262
262
|
options.target,
|
|
@@ -264,131 +264,169 @@ async function scaffold(options) {
|
|
|
264
264
|
options.version ?? cliVersion(),
|
|
265
265
|
modules
|
|
266
266
|
);
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
-
...files.filter((
|
|
270
|
-
...
|
|
267
|
+
const removed = /* @__PURE__ */ new Set(["src/router.tsx", "client/src/router.tsx"]);
|
|
268
|
+
const listed = /* @__PURE__ */ new Set([
|
|
269
|
+
...files.filter((file) => keepsRoutes(modules) || !removed.has(file)),
|
|
270
|
+
...laid
|
|
271
271
|
]);
|
|
272
|
-
return { files: [...
|
|
272
|
+
return { files: [...listed].sort() };
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
// src/commands/create.ts
|
|
276
|
-
var
|
|
277
|
-
var
|
|
276
|
+
var LAST_LINES = 8;
|
|
277
|
+
var LINE_WIDTH = 64;
|
|
278
278
|
var TEMPLATE_LABELS = {
|
|
279
279
|
"react-ts": "Single-page app \u2014 React, TypeScript, Odoro router and motion",
|
|
280
280
|
"react-ts-server": "Client and server \u2014 the same app, plus a modular @odoro-cli/server base and a Dockerfile"
|
|
281
281
|
};
|
|
282
|
-
async function
|
|
282
|
+
async function provisionOnPlatform(projectName) {
|
|
283
|
+
const load = await loadSdk();
|
|
284
|
+
if (!load.ok) {
|
|
285
|
+
prompts.log.warn(load.reason);
|
|
286
|
+
return void 0;
|
|
287
|
+
}
|
|
288
|
+
const token = await ensureToken(prompts, DEFAULT_API_URL);
|
|
289
|
+
if (cancelled(token)) return void 0;
|
|
290
|
+
const client = load.sdk.createClient({ baseUrl: DEFAULT_API_URL, token });
|
|
291
|
+
const environment = await chooseEnvironment(prompts, client);
|
|
292
|
+
if (cancelled(environment) || environment === void 0) return void 0;
|
|
293
|
+
const region = await chooseRegion(prompts, client);
|
|
294
|
+
if (cancelled(region)) return void 0;
|
|
295
|
+
const spinner2 = prompts.spinner();
|
|
296
|
+
spinner2.start(`Provisioning in ${region ?? "the default region"}`);
|
|
297
|
+
const result = await provision({
|
|
298
|
+
client,
|
|
299
|
+
environmentId: environment.id,
|
|
300
|
+
...region === void 0 ? {} : { region },
|
|
301
|
+
name: projectName
|
|
302
|
+
});
|
|
303
|
+
if (!result.ok) {
|
|
304
|
+
spinner2.stop("Provisioning did not finish");
|
|
305
|
+
prompts.log.warn(result.reason);
|
|
306
|
+
return void 0;
|
|
307
|
+
}
|
|
308
|
+
spinner2.stop(`Database ready in ${environment.label}`);
|
|
309
|
+
return {
|
|
310
|
+
choice: "provider",
|
|
311
|
+
url: result.connectionString,
|
|
312
|
+
note: `Database provisioned on ${environment.label}. DATABASE_URL is in .env \u2014 do not commit it.`
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
async function askDatabase(projectName) {
|
|
283
316
|
const choice = ensure(
|
|
284
317
|
await prompts.select({
|
|
285
318
|
message: "Database",
|
|
286
|
-
initialValue: "
|
|
319
|
+
initialValue: "provider",
|
|
287
320
|
options: [
|
|
288
321
|
{
|
|
289
322
|
value: "provider",
|
|
290
323
|
label: "Odoro provider",
|
|
291
|
-
hint: "
|
|
324
|
+
hint: "provisioned now, PostgreSQL, region of your choosing"
|
|
292
325
|
},
|
|
293
326
|
{
|
|
294
327
|
value: "url",
|
|
295
328
|
label: "Existing PostgreSQL URL",
|
|
296
|
-
hint: "
|
|
329
|
+
hint: "bring your own"
|
|
297
330
|
},
|
|
298
331
|
{ value: "later", label: "Set up later", hint: ".env.example only" }
|
|
299
332
|
]
|
|
300
333
|
})
|
|
301
334
|
);
|
|
302
335
|
if (choice === "provider") {
|
|
303
|
-
|
|
304
|
-
|
|
336
|
+
const provisioned = await provisionOnPlatform(projectName);
|
|
337
|
+
if (provisioned !== void 0) return provisioned;
|
|
338
|
+
return { choice, note: PROVIDER_PENDING };
|
|
305
339
|
}
|
|
306
340
|
if (choice === "later") {
|
|
307
341
|
return {
|
|
308
342
|
choice,
|
|
309
|
-
note: "
|
|
343
|
+
note: "No database: the client will start, and /api/ready will answer 503 saying what is missing."
|
|
310
344
|
};
|
|
311
345
|
}
|
|
312
346
|
const url = ensure(
|
|
313
347
|
await prompts.text({
|
|
314
348
|
message: "PostgreSQL URL",
|
|
315
|
-
placeholder: "postgres://
|
|
349
|
+
placeholder: "postgres://user:password@host:5432/database?sslmode=require",
|
|
316
350
|
validate: (value) => checkDatabaseUrl(value ?? "")
|
|
317
351
|
})
|
|
318
352
|
);
|
|
319
353
|
return {
|
|
320
354
|
choice,
|
|
321
355
|
url: url.trim(),
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
note: "
|
|
356
|
+
// Only the shape was checked: say so, rather than letting one believe the
|
|
357
|
+
// connection was established.
|
|
358
|
+
note: "URL shape checked. The connection will be established on the first start."
|
|
325
359
|
};
|
|
326
360
|
}
|
|
327
361
|
async function askModules(options) {
|
|
328
362
|
if (options.modules !== void 0) {
|
|
329
|
-
const
|
|
330
|
-
if (
|
|
331
|
-
prompts.cancel(
|
|
363
|
+
const read = readModules(options.modules);
|
|
364
|
+
if (read.error !== void 0) {
|
|
365
|
+
prompts.cancel(read.error);
|
|
332
366
|
return void 0;
|
|
333
367
|
}
|
|
334
|
-
return
|
|
368
|
+
return announce(resolveModules(read.modules));
|
|
335
369
|
}
|
|
336
|
-
if (options.yes === true) return
|
|
337
|
-
const
|
|
370
|
+
if (options.yes === true) return DEFAULT_MODULES;
|
|
371
|
+
const chosen = ensure(
|
|
338
372
|
await prompts.multiselect({
|
|
339
373
|
message: "What goes in the project?",
|
|
340
|
-
initialValues: [...
|
|
374
|
+
initialValues: [...DEFAULT_MODULES],
|
|
341
375
|
required: false,
|
|
342
|
-
options: MODULES.map((
|
|
376
|
+
options: MODULES.map((module) => ({
|
|
377
|
+
value: module.id,
|
|
378
|
+
label: module.label,
|
|
379
|
+
hint: module.hint
|
|
380
|
+
}))
|
|
343
381
|
})
|
|
344
382
|
);
|
|
345
|
-
return
|
|
383
|
+
return announce(resolveModules(chosen));
|
|
346
384
|
}
|
|
347
|
-
function
|
|
348
|
-
for (const
|
|
385
|
+
function announce(resolution) {
|
|
386
|
+
for (const warning of resolution.warnings) prompts.log.warn(warning);
|
|
349
387
|
return resolution.modules;
|
|
350
388
|
}
|
|
351
|
-
async function
|
|
352
|
-
const [
|
|
353
|
-
if (
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
const
|
|
357
|
-
const
|
|
358
|
-
for (const
|
|
359
|
-
const
|
|
360
|
-
if (
|
|
361
|
-
|
|
362
|
-
if (
|
|
363
|
-
|
|
389
|
+
async function installDependencies(target, manager) {
|
|
390
|
+
const [command, ...args] = installCommand(manager).split(" ");
|
|
391
|
+
if (command === void 0) return false;
|
|
392
|
+
const bar = prompts.spinner({ indicator: "timer" });
|
|
393
|
+
bar.start(`Installing dependencies with ${manager}`);
|
|
394
|
+
const tail = [];
|
|
395
|
+
const keep = (block) => {
|
|
396
|
+
for (const line of block.split("\n")) {
|
|
397
|
+
const clean = line.trim();
|
|
398
|
+
if (clean === "" || !/[a-z0-9]/i.test(clean)) continue;
|
|
399
|
+
tail.push(clean);
|
|
400
|
+
if (tail.length > LAST_LINES) tail.shift();
|
|
401
|
+
bar.message(`${manager} \xB7 ${clean.slice(0, LINE_WIDTH)}`);
|
|
364
402
|
}
|
|
365
403
|
};
|
|
366
404
|
const code = await new Promise((resolve_) => {
|
|
367
|
-
const
|
|
405
|
+
const child = spawn(command, args, {
|
|
368
406
|
cwd: target,
|
|
369
407
|
shell: process.platform === "win32"
|
|
370
408
|
});
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
409
|
+
child.stdout?.setEncoding("utf8").on("data", keep);
|
|
410
|
+
child.stderr?.setEncoding("utf8").on("data", keep);
|
|
411
|
+
child.on("error", () => {
|
|
374
412
|
resolve_(-1);
|
|
375
413
|
});
|
|
376
|
-
|
|
377
|
-
resolve_(
|
|
414
|
+
child.on("close", (status) => {
|
|
415
|
+
resolve_(status ?? -1);
|
|
378
416
|
});
|
|
379
417
|
});
|
|
380
418
|
if (code === 0) {
|
|
381
|
-
|
|
419
|
+
bar.stop(`Dependencies installed with ${manager}`);
|
|
382
420
|
return true;
|
|
383
421
|
}
|
|
384
|
-
|
|
385
|
-
for (const
|
|
422
|
+
bar.stop(`${manager} install failed`, 1);
|
|
423
|
+
for (const line of tail) prompts.log.error(colors.dim(line));
|
|
386
424
|
return false;
|
|
387
425
|
}
|
|
388
|
-
function
|
|
426
|
+
function cliVersion2() {
|
|
389
427
|
try {
|
|
390
|
-
const
|
|
391
|
-
const { version } = JSON.parse(readFileSync(
|
|
428
|
+
const manifest = join(dirname(templatesRoot()), "package.json");
|
|
429
|
+
const { version } = JSON.parse(readFileSync(manifest, "utf8"));
|
|
392
430
|
return version;
|
|
393
431
|
} catch {
|
|
394
432
|
return "?";
|
|
@@ -406,12 +444,12 @@ async function createCommand(options) {
|
|
|
406
444
|
const templates = availableTemplates(root);
|
|
407
445
|
const defaultTemplate = templates[0] ?? "react-ts";
|
|
408
446
|
prompts.intro(
|
|
409
|
-
`${colors.bgBlue(colors.black(" ODORO "))} ${colors.dim(`v${
|
|
447
|
+
`${colors.bgBlue(colors.black(" ODORO "))} ${colors.dim(`v${cliVersion2()}`)}`
|
|
410
448
|
);
|
|
411
449
|
const rawName = options.name ?? (options.yes === true ? "odoro-app" : ensure(
|
|
412
450
|
await prompts.text({
|
|
413
451
|
message: "Project name",
|
|
414
|
-
placeholder: "
|
|
452
|
+
placeholder: "my-site",
|
|
415
453
|
defaultValue: "odoro-app",
|
|
416
454
|
validate: (value) => value === "" ? void 0 : validatePackageName(toPackageName(value))
|
|
417
455
|
})
|
|
@@ -425,7 +463,7 @@ async function createCommand(options) {
|
|
|
425
463
|
}
|
|
426
464
|
let overwrite = options.overwrite;
|
|
427
465
|
const state = inspectTarget(target);
|
|
428
|
-
if (state === "
|
|
466
|
+
if (state === "occupied" && overwrite === void 0) {
|
|
429
467
|
if (options.yes === true) {
|
|
430
468
|
prompts.cancel(
|
|
431
469
|
`Folder "${basename(target)}" is not empty. Pass --overwrite or --merge.`
|
|
@@ -436,14 +474,14 @@ async function createCommand(options) {
|
|
|
436
474
|
await prompts.select({
|
|
437
475
|
message: `Folder "${basename(target)}" is not empty.`,
|
|
438
476
|
options: [
|
|
439
|
-
{ value: "
|
|
477
|
+
{ value: "cancel", label: "Cancel" },
|
|
440
478
|
{ value: "fusionner", label: "Merge \u2014 overwrites files of the same name" },
|
|
441
479
|
{ value: "ecraser", label: "Empty the folder, then create the project" }
|
|
442
480
|
]
|
|
443
481
|
})
|
|
444
482
|
);
|
|
445
|
-
if (choice === "
|
|
446
|
-
prompts.cancel("Creation
|
|
483
|
+
if (choice === "cancel") {
|
|
484
|
+
prompts.cancel("Creation cancelled.");
|
|
447
485
|
return 0;
|
|
448
486
|
}
|
|
449
487
|
overwrite = choice;
|
|
@@ -481,7 +519,7 @@ async function createCommand(options) {
|
|
|
481
519
|
prompts.cancel(`Unknown package manager: "${manager}".`);
|
|
482
520
|
return 1;
|
|
483
521
|
}
|
|
484
|
-
const database = template === "react-ts-server" && options.yes !== true ? await askDatabase() : void 0;
|
|
522
|
+
const database = template === "react-ts-server" && options.yes !== true ? await askDatabase(packageName) : void 0;
|
|
485
523
|
const withGit = options.git ?? (options.yes === true ? true : ensure(await prompts.confirm({ message: "Initialize a git repository?" })));
|
|
486
524
|
const withInstall = options.install ?? (options.yes === true ? true : ensure(
|
|
487
525
|
await prompts.confirm({
|
|
@@ -513,12 +551,12 @@ async function createCommand(options) {
|
|
|
513
551
|
if (database?.url !== void 0) {
|
|
514
552
|
await writeDatabaseUrl(target, database.url);
|
|
515
553
|
prompts.log.success("Database URL written to .env");
|
|
516
|
-
const
|
|
517
|
-
if (
|
|
554
|
+
const risk = await assertEnvIgnored(target);
|
|
555
|
+
if (risk !== void 0) prompts.log.warn(risk);
|
|
518
556
|
}
|
|
519
557
|
if (database !== void 0) prompts.log.info(database.note);
|
|
520
558
|
if (modules.includes("registre")) {
|
|
521
|
-
const { initCommand } = await import('./commands-
|
|
559
|
+
const { initCommand } = await import('./commands-GYA7NTCX.js');
|
|
522
560
|
const code = await initCommand({ root: target, yes: true });
|
|
523
561
|
if (code !== 0) {
|
|
524
562
|
prompts.log.warn(
|
|
@@ -526,14 +564,14 @@ async function createCommand(options) {
|
|
|
526
564
|
);
|
|
527
565
|
}
|
|
528
566
|
}
|
|
529
|
-
const
|
|
567
|
+
const installed = withInstall ? await installDependencies(target, manager) : false;
|
|
530
568
|
const steps = [
|
|
531
569
|
`cd ${basename(target)}`,
|
|
532
|
-
//
|
|
533
|
-
//
|
|
534
|
-
...
|
|
535
|
-
// `odoro.json`
|
|
536
|
-
//
|
|
570
|
+
// When the install failed, the command comes back into the steps: the
|
|
571
|
+
// project is written, it is only missing its dependencies.
|
|
572
|
+
...installed ? [] : [installCommand(manager)],
|
|
573
|
+
// `odoro.json` has just been written: what remains to show is the command
|
|
574
|
+
// that uses it.
|
|
537
575
|
...modules.includes("registre") ? ["odoro add text/count-up"] : [],
|
|
538
576
|
runCommand(manager, "dev")
|
|
539
577
|
];
|