@aventara/cli 1.0.0-rc.4 → 1.0.0-rc.9

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.
@@ -1,46 +1,10 @@
1
- import { spliceAppModule, wiringInstructions, } from "../apply/app-module.anchor.js";
2
- import { E2E_SPEC, spliceContractTest } from "../apply/e2e-spec.anchor.js";
3
- import { CORS_ORIGINS_VARIABLE, MAIN_FILE, mainCorsInstructions, SCAFFOLD_CORS_ORIGINS, spliceMainCors, } from "../apply/main-cors.anchor.js";
4
1
  import { appendEnvEntryIfAbsent, mergeEnvFile, mergeGitignore, mergePackageManifest, mergePnpmWorkspace, } from "../apply/manifest.merger.js";
5
- import { APP_MODULE, } from "../project/project.inspector.js";
6
- import { planAgentSkill } from "../skill/agent-skill.planner.js";
7
- import { SCAFFOLD_API, } from "../templates/template.registry.js";
8
- export const PREPARE_SCRIPT = "aventara:prepare";
9
- const PREPARED_SCRIPTS = [
10
- "build",
11
- "start",
12
- "start:dev",
13
- "start:debug",
14
- ];
15
- const START_SCRIPTS = {
16
- start: { from: "nest start", to: "nest start --env-file .env" },
17
- "start:dev": {
18
- from: "nest start --watch",
19
- to: "nest start --watch --env-file .env",
20
- },
21
- "start:debug": {
22
- from: "nest start --debug --watch",
23
- to: "nest start --debug --watch --env-file .env",
24
- },
25
- "start:prod": {
26
- from: "node dist/main",
27
- to: "node --env-file-if-exists=.env dist/main",
28
- },
29
- };
30
- const E2E_SCRIPTS = {
31
- esm: {
32
- from: "vitest run --config ./vitest.config.e2e.ts",
33
- to: "node --env-file-if-exists=.env ./node_modules/vitest/vitest.mjs run --config ./vitest.config.e2e.ts",
34
- },
35
- cjs: {
36
- from: "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --config ./test/jest-e2e.json",
37
- to: "node --env-file-if-exists=.env --experimental-vm-modules ./node_modules/jest/bin/jest.js --config ./test/jest-e2e.json",
38
- },
39
- };
40
- const NEST_HOST = "@aventara/nest";
2
+ import { planAgentSkill, SKILL_DIRECTORY, } from "../skill/agent-skill.planner.js";
3
+ import { PREPARE_SCRIPT, } from "./host-plan.interface.js";
4
+ import { planNestHost } from "./nest-host.planner.js";
5
+ import { planNextHost } from "./next-host.planner.js";
41
6
  const CORE = "@aventara/core";
42
7
  const BUILD = "@aventara/build";
43
- const BUILD_BIN = "aventara-build";
44
8
  function writeOf(path, before, after) {
45
9
  return before === after
46
10
  ? []
@@ -52,152 +16,109 @@ function writeOf(path, before, after) {
52
16
  },
53
17
  ];
54
18
  }
55
- export function frontendInitCommand(packageManager, cliVersion) {
56
- const tag = /^\d+\.\d+\.\d+-([0-9A-Za-z-]+)/.exec(cliVersion)?.[1];
57
- const runner = packageManager === "pnpm" ? "pnpm dlx" : "npx";
58
- return `${runner} @aventara/client${tag === undefined ? "" : `@${tag}`} init`;
59
- }
60
19
  export function planInit(input) {
61
- const { inspection, scaffold, packageManager } = input;
20
+ const { inspection, hosted, packageManager } = input;
21
+ const { scaffold } = hosted;
62
22
  const conflicts = new Set();
63
23
  const keeping = [];
64
24
  const replacing = [];
65
25
  const warnings = [...(input.warnings ?? [])];
66
- const both = (path, before, merge) => {
67
- const kept = merge(false);
68
- const replaced = merge(true);
69
- for (const conflict of replaced.conflicts) {
70
- conflicts.add(conflict);
71
- }
72
- keeping.push(...writeOf(path, before, kept.text));
73
- replacing.push(...writeOf(path, before, replaced.text));
26
+ const instructions = [];
27
+ const builder = {
28
+ read: input.read,
29
+ file: (path, content) => {
30
+ const before = input.read(path);
31
+ if (before === undefined) {
32
+ keeping.push({ path, content, change: "create" });
33
+ replacing.push({ path, content, change: "create" });
34
+ }
35
+ else if (before !== content) {
36
+ conflicts.add(path);
37
+ replacing.push({ path, content, change: "replace" });
38
+ }
39
+ },
40
+ write: (path, before, after) => {
41
+ keeping.push(...writeOf(path, before, after));
42
+ replacing.push(...writeOf(path, before, after));
43
+ },
44
+ both: (path, before, merge) => {
45
+ const kept = merge(false);
46
+ const replaced = merge(true);
47
+ for (const conflict of replaced.conflicts) {
48
+ conflicts.add(conflict);
49
+ }
50
+ keeping.push(...writeOf(path, before, kept.text));
51
+ replacing.push(...writeOf(path, before, replaced.text));
52
+ },
53
+ warn: (warning) => {
54
+ warnings.push(warning);
55
+ },
56
+ instruct: (text) => {
57
+ instructions.push(text);
58
+ },
74
59
  };
75
60
  for (const file of scaffold.files) {
76
- const before = input.read(file.path);
77
- if (before === undefined) {
78
- keeping.push({
79
- path: file.path,
80
- content: file.content,
81
- change: "create",
82
- });
83
- replacing.push({
84
- path: file.path,
85
- content: file.content,
86
- change: "create",
87
- });
88
- }
89
- else if (before !== file.content) {
90
- conflicts.add(file.path);
91
- replacing.push({
92
- path: file.path,
93
- content: file.content,
94
- change: "replace",
95
- });
96
- }
61
+ builder.file(file.path, file.content);
97
62
  }
98
- const wiring = {
99
- imports: [
100
- `import { AventaraModule } from '${NEST_HOST}';`,
101
- ...scaffold.appModule.imports,
102
- ],
103
- entries: scaffold.appModule.entries,
104
- };
105
- const appModule = input.read(APP_MODULE);
106
- const edit = input.wiringByHand !== undefined
107
- ? { kind: "not-anchored", reason: input.wiringByHand }
108
- : appModule === undefined
109
- ? {
110
- kind: "not-anchored",
111
- reason: `there is no ${APP_MODULE}`,
112
- }
113
- : spliceAppModule(appModule, wiring);
114
- let instructions;
115
- if (edit.kind === "edited") {
116
- keeping.push(...writeOf(APP_MODULE, appModule, edit.text));
117
- replacing.push(...writeOf(APP_MODULE, appModule, edit.text));
118
- const spec = input.read(E2E_SPEC);
119
- const tested = spec === undefined ? undefined : spliceContractTest(spec, SCAFFOLD_API);
120
- if (tested !== undefined) {
121
- keeping.push(...writeOf(E2E_SPEC, spec, tested));
122
- replacing.push(...writeOf(E2E_SPEC, spec, tested));
123
- }
124
- }
125
- else {
126
- instructions = wiringInstructions(wiring);
127
- warnings.push(`${APP_MODULE} was not edited: ${edit.reason}; add AventaraModule by hand, as printed below`);
128
- }
129
- const main = input.read(MAIN_FILE);
130
- const cors = main === undefined
131
- ? { kind: "not-anchored", reason: `there is no ${MAIN_FILE}` }
132
- : spliceMainCors(main);
133
- if (cors.kind === "edited") {
134
- keeping.push(...writeOf(MAIN_FILE, main, cors.text));
135
- replacing.push(...writeOf(MAIN_FILE, main, cors.text));
136
- }
137
- else if (cors.kind === "configured") {
138
- warnings.push(`${MAIN_FILE} already configures CORS, so it was left as it is; a browser frontend on another origin is answered only if that configuration allows it`);
139
- }
140
- else if (cors.kind === "not-anchored") {
141
- instructions = [instructions, mainCorsInstructions()]
142
- .filter((text) => text !== undefined)
143
- .join("\n");
144
- warnings.push(`${MAIN_FILE} was not edited: ${cors.reason}; add CORS by hand, as printed below`);
145
- }
146
- const corsWanted = cors.kind !== "configured";
63
+ const host = hosted.host === "nest"
64
+ ? planNestHost(builder, {
65
+ inspection,
66
+ scaffold: hosted.scaffold,
67
+ packageManager,
68
+ aventaraVersion: input.aventaraVersion,
69
+ ...(hosted.wiringByHand === undefined
70
+ ? {}
71
+ : { wiringByHand: hosted.wiringByHand }),
72
+ })
73
+ : planNextHost(builder, {
74
+ inspection,
75
+ packageManager,
76
+ aventaraVersion: input.aventaraVersion,
77
+ externalPackages: [CORE, input.entry.adapterPackage],
78
+ });
147
79
  const scaffoldWritesEnv = Object.keys(scaffold.env).length > 0;
148
- const env = input.read(".env");
149
- const corsStep = corsWanted && !scaffoldWritesEnv && env === undefined
150
- ? [
151
- `Let a browser frontend call the server: add ${CORS_ORIGINS_VARIABLE}="${SCAFFOLD_CORS_ORIGINS}" to .env (unset, CORS stays off)`,
152
- ]
153
- : [];
154
- const versions = {
155
- [CORE]: input.aventaraVersion,
156
- [NEST_HOST]: input.aventaraVersion,
157
- [input.entry.adapterPackage]: input.aventaraVersion,
158
- };
159
- both("package.json", inspection.manifestText, (replace) => mergePackageManifest(inspection.manifestText, {
160
- dependencies: { ...versions, ...scaffold.dependencies },
80
+ builder.both("package.json", inspection.manifestText, (replace) => mergePackageManifest(inspection.manifestText, {
81
+ dependencies: {
82
+ [CORE]: input.aventaraVersion,
83
+ ...host.dependencies,
84
+ [input.entry.adapterPackage]: input.aventaraVersion,
85
+ ...scaffold.dependencies,
86
+ },
161
87
  devDependencies: {
162
88
  [BUILD]: input.aventaraVersion,
163
89
  ...scaffold.devDependencies,
164
90
  },
165
91
  scripts: {
166
- [PREPARE_SCRIPT]: `${scaffold.prepare} && ${BUILD_BIN}`,
92
+ [PREPARE_SCRIPT]: `${scaffold.prepare} && ${host.build}`,
167
93
  postinstall: `${packageManager} run ${PREPARE_SCRIPT}`,
168
- ...Object.fromEntries(PREPARED_SCRIPTS.map((script) => [
169
- `pre${script}`,
170
- `${packageManager} run ${PREPARE_SCRIPT}`,
171
- ])),
94
+ ...host.scripts,
172
95
  },
173
- rewrites: inspection.usesNestConfig
174
- ? {}
175
- : {
176
- ...START_SCRIPTS,
177
- "test:e2e": E2E_SCRIPTS[inspection.moduleKind],
178
- },
96
+ rewrites: host.rewrites,
179
97
  }, replace));
180
- if (scaffoldWritesEnv || (corsWanted && env !== undefined)) {
181
- both(".env", env, (replace) => {
98
+ const env = input.read(".env");
99
+ const { envEntry } = host;
100
+ if (scaffoldWritesEnv || (envEntry !== undefined && env !== undefined)) {
101
+ builder.both(".env", env, (replace) => {
182
102
  const merged = scaffoldWritesEnv
183
103
  ? mergeEnvFile(env, scaffold.env, replace)
184
104
  : { text: env ?? "", conflicts: [] };
185
- return corsWanted
186
- ? {
187
- text: appendEnvEntryIfAbsent(merged.text, CORS_ORIGINS_VARIABLE, SCAFFOLD_CORS_ORIGINS),
105
+ return envEntry === undefined
106
+ ? merged
107
+ : {
108
+ text: appendEnvEntryIfAbsent(merged.text, envEntry.key, envEntry.value),
188
109
  conflicts: merged.conflicts,
189
- }
190
- : merged;
110
+ };
191
111
  });
192
112
  }
193
113
  const gitignore = input.read(".gitignore");
194
- both(".gitignore", gitignore, () => mergeGitignore(gitignore, [
114
+ builder.both(".gitignore", gitignore, () => mergeGitignore(gitignore, [
195
115
  ...scaffold.gitignore,
116
+ ...host.gitignore,
196
117
  ...(scaffoldWritesEnv ? [".env"] : []),
197
118
  ]));
198
119
  if (packageManager === "pnpm" && scaffold.nativeBuilds.length > 0) {
199
120
  const workspace = input.read("pnpm-workspace.yaml");
200
- both("pnpm-workspace.yaml", workspace, () => mergePnpmWorkspace(workspace, scaffold.nativeBuilds));
121
+ builder.both("pnpm-workspace.yaml", workspace, () => mergePnpmWorkspace(workspace, scaffold.nativeBuilds));
201
122
  }
202
123
  const skill = planAgentSkill({
203
124
  read: input.read,
@@ -213,20 +134,14 @@ export function planInit(input) {
213
134
  replacing,
214
135
  conflicts: [...conflicts],
215
136
  warnings,
216
- instructions,
137
+ instructions: instructions.length === 0 ? undefined : instructions.join("\n"),
217
138
  nextSteps: [
218
139
  ...(input.skipInstall
219
140
  ? [`Install, which also runs ${PREPARE_SCRIPT}: ${run} install`]
220
141
  : []),
221
142
  ...scaffold.nextSteps,
222
- ...(inspection.usesNestConfig
223
- ? [
224
- "The start scripts were left as they are: @nestjs/config loads .env, so make sure it does before Prisma connects.",
225
- ]
226
- : []),
227
- ...corsStep,
228
- `Start the server: ${run} run start:dev`,
229
- `In your frontend: ${frontendInitCommand(packageManager, input.aventaraVersion)}`,
143
+ ...host.nextSteps,
144
+ `Call the API with curl: see "Calling the API over HTTP" in ${SKILL_DIRECTORY}/SKILL.md`,
230
145
  ],
231
146
  };
232
147
  }
@@ -1,12 +1,14 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import path from "node:path";
3
+ import { frameworkMajorOf, HOSTS, } from "../catalog/host-entry.interface.js";
3
4
  import { lowestMajorOf } from "../catalog/range.reader.js";
4
5
  export class AvProjectRefusedError extends Error {
5
6
  name = "AvProjectRefusedError";
6
7
  }
7
- const NEST_MAJOR = 12;
8
8
  const FIRST_UNSUPPORTED_TYPESCRIPT = 7;
9
9
  export const APP_MODULE = "src/app.module.ts";
10
+ const NEXT_APP_DIRECTORY = "src/app";
11
+ const SOURCE_ALIAS = /"@\/\*"\s*:\s*\[\s*"\.\/src\/\*"\s*\]/;
10
12
  const LOCKFILES = [
11
13
  ["package-lock.json", "npm"],
12
14
  ["pnpm-lock.yaml", "pnpm"],
@@ -20,19 +22,59 @@ function matchesFamily(pattern, name) {
20
22
  ? name.startsWith(pattern.slice(0, -1))
21
23
  : name === pattern;
22
24
  }
25
+ function servedProjects() {
26
+ const named = HOSTS.map((host) => `${host.label} ${frameworkMajorOf(host)}`);
27
+ return named.length <= 1
28
+ ? named.join("")
29
+ : `${named.slice(0, -1).join(", ")} or ${named[named.length - 1]}`;
30
+ }
31
+ function detectHost(manifest) {
32
+ const found = HOSTS.filter((host) => dependencyRange(manifest, host.framework.package) !== undefined);
33
+ const frameworks = HOSTS.map((host) => host.framework.package);
34
+ if (found.length === 0) {
35
+ throw new AvProjectRefusedError(`aventara init runs in a ${servedProjects()} project, and this package.json depends on ${frameworks.length === 2
36
+ ? `neither ${frameworks[0]} nor ${frameworks[1]}`
37
+ : `none of ${frameworks.join(", ")}`}`);
38
+ }
39
+ if (found.length > 1) {
40
+ throw new AvProjectRefusedError(`this package.json depends on both ${found.map((host) => host.framework.package).join(" and ")}, so aventara init cannot tell whether it is a ${found.map((host) => host.label).join(" or a ")} project; Aventara is hosted by one of them`);
41
+ }
42
+ return found[0];
43
+ }
44
+ const HOST_REFUSALS = {
45
+ nest: (directory) => {
46
+ const appModule = path.join(directory, APP_MODULE);
47
+ if (existsSync(appModule) &&
48
+ /\bAventaraModule\b/.test(readFileSync(appModule, "utf8"))) {
49
+ throw new AvProjectRefusedError(`this project is already initialized: ${APP_MODULE} uses AventaraModule`);
50
+ }
51
+ },
52
+ next: (directory, manifest, host) => {
53
+ if (dependencyRange(manifest, host.package) !== undefined) {
54
+ throw new AvProjectRefusedError(`this project is already initialized: package.json depends on ${host.package}`);
55
+ }
56
+ if (!existsSync(path.join(directory, NEXT_APP_DIRECTORY))) {
57
+ throw new AvProjectRefusedError(`aventara init serves a Next.js app built with the App Router in ${NEXT_APP_DIRECTORY}/, and this project has no ${NEXT_APP_DIRECTORY}/ (the Pages Router, and an app/ outside src/, are not served)`);
58
+ }
59
+ const tsconfig = path.join(directory, "tsconfig.json");
60
+ if (!existsSync(tsconfig) ||
61
+ !SOURCE_ALIAS.test(readFileSync(tsconfig, "utf8"))) {
62
+ throw new AvProjectRefusedError('the scaffold imports its server entry as @/aventara/server, and this project\'s tsconfig.json maps no "@/*" to "./src/*"; add "paths": { "@/*": ["./src/*"] } to its compilerOptions, as create-next-app writes it');
63
+ }
64
+ },
65
+ };
23
66
  export function inspectProject(directory, catalog) {
24
67
  const manifestPath = path.join(directory, "package.json");
25
68
  if (!existsSync(manifestPath)) {
26
- throw new AvProjectRefusedError(`aventara init runs in a NestJS ${NEST_MAJOR} project, and there is no package.json in ${directory}`);
69
+ throw new AvProjectRefusedError(`aventara init runs in a ${servedProjects()} project, and there is no package.json in ${directory}`);
27
70
  }
28
71
  const manifestText = readFileSync(manifestPath, "utf8");
29
72
  const manifest = JSON.parse(manifestText);
30
- const nest = dependencyRange(manifest, "@nestjs/core");
31
- if (nest === undefined) {
32
- throw new AvProjectRefusedError(`aventara init runs in a NestJS ${NEST_MAJOR} project, and this package.json has no @nestjs/core dependency`);
33
- }
34
- if (lowestMajorOf(nest) !== NEST_MAJOR) {
35
- throw new AvProjectRefusedError(`aventara init supports NestJS ${NEST_MAJOR} only, and this project depends on @nestjs/core ${nest}`);
73
+ const host = detectHost(manifest);
74
+ const declared = dependencyRange(manifest, host.framework.package);
75
+ const major = frameworkMajorOf(host);
76
+ if (lowestMajorOf(declared) !== major) {
77
+ throw new AvProjectRefusedError(`aventara init supports ${host.label} ${major} only, and this project depends on ${host.framework.package} ${declared}`);
36
78
  }
37
79
  const typescript = dependencyRange(manifest, "typescript");
38
80
  const typescriptMajor = typescript === undefined ? undefined : lowestMajorOf(typescript);
@@ -45,11 +87,7 @@ export function inspectProject(directory, catalog) {
45
87
  if (lockfiles.includes("yarn") && known.length === 0) {
46
88
  throw new AvProjectRefusedError("this project has only a yarn.lock, and aventara init supports npm and pnpm");
47
89
  }
48
- const appModule = path.join(directory, APP_MODULE);
49
- if (existsSync(appModule) &&
50
- /\bAventaraModule\b/.test(readFileSync(appModule, "utf8"))) {
51
- throw new AvProjectRefusedError(`this project is already initialized: ${APP_MODULE} uses AventaraModule`);
52
- }
90
+ HOST_REFUSALS[host.alias](directory, manifest, host);
53
91
  const dependencies = [
54
92
  ...Object.keys(manifest.dependencies ?? {}),
55
93
  ...Object.keys(manifest.devDependencies ?? {}),
@@ -64,6 +102,7 @@ export function inspectProject(directory, catalog) {
64
102
  };
65
103
  return {
66
104
  directory,
105
+ host,
67
106
  manifest,
68
107
  manifestText,
69
108
  moduleKind: manifest.type === "module" ? "esm" : "cjs",
@@ -1,22 +1,40 @@
1
1
  import { spawn } from "node:child_process";
2
- export const runCommand = (command, args, options) => new Promise((resolve, reject) => {
3
- const child = spawn(command, args, {
4
- cwd: options.cwd,
5
- stdio: ["ignore", "pipe", "pipe"],
6
- env: process.env,
2
+ export const runCommand = (command, args, options) => options.interactive === true
3
+ ? runInteractive(command, args, options.cwd)
4
+ : runPiped(command, args, options);
5
+ function runInteractive(command, args, cwd) {
6
+ return new Promise((resolve, reject) => {
7
+ const child = spawn(command, args, {
8
+ cwd,
9
+ stdio: "inherit",
10
+ env: process.env,
11
+ });
12
+ child.once("error", reject);
13
+ child.once("close", (code) => {
14
+ resolve({ code: code ?? 1, stdout: "", stderr: "" });
15
+ });
7
16
  });
8
- let stdout = "";
9
- let stderr = "";
10
- child.stdout.setEncoding("utf8").on("data", (chunk) => {
11
- stdout += chunk;
12
- options.onOutput?.(chunk);
17
+ }
18
+ function runPiped(command, args, options) {
19
+ return new Promise((resolve, reject) => {
20
+ const child = spawn(command, args, {
21
+ cwd: options.cwd,
22
+ stdio: ["ignore", "pipe", "pipe"],
23
+ env: process.env,
24
+ });
25
+ let stdout = "";
26
+ let stderr = "";
27
+ child.stdout.setEncoding("utf8").on("data", (chunk) => {
28
+ stdout += chunk;
29
+ options.onOutput?.(chunk);
30
+ });
31
+ child.stderr.setEncoding("utf8").on("data", (chunk) => {
32
+ stderr += chunk;
33
+ options.onOutput?.(chunk);
34
+ });
35
+ child.once("error", reject);
36
+ child.once("close", (code) => {
37
+ resolve({ code: code ?? 1, stdout, stderr });
38
+ });
13
39
  });
14
- child.stderr.setEncoding("utf8").on("data", (chunk) => {
15
- stderr += chunk;
16
- options.onOutput?.(chunk);
17
- });
18
- child.once("error", reject);
19
- child.once("close", (code) => {
20
- resolve({ code: code ?? 1, stdout, stderr });
21
- });
22
- });
40
+ }
@@ -6,7 +6,7 @@ import { planInit } from "../plan/project.planner.js";
6
6
  import { detectPackageManager } from "../project/package-manager.detector.js";
7
7
  import { AvProjectRefusedError, inspectProject, } from "../project/project.inspector.js";
8
8
  import { scanServices } from "../project/service.detector.js";
9
- import { templatesFor, } from "../templates/template.registry.js";
9
+ import { templatesFor } from "../templates/template.registry.js";
10
10
  import { resolveAnswers } from "../wizard/answer.resolver.js";
11
11
  import { INIT_QUESTIONS, REUSE_QUESTIONS, } from "../wizard/wizard.questions.js";
12
12
  import { createStepReporter, } from "./step.reporter.js";
@@ -39,18 +39,15 @@ export async function initProject(command, io) {
39
39
  const prepared = inspection.orm.kind === "present"
40
40
  ? await prepareReuse(command, io, inspection, detectedPackageManager)
41
41
  : await prepareFresh(command, io, inspection, detectedPackageManager);
42
- const { entry, provider, scaffold, packageManager } = prepared;
42
+ const { entry, provider, hosted, packageManager } = prepared;
43
43
  const plan = planInit({
44
44
  inspection,
45
45
  entry,
46
- scaffold,
46
+ hosted,
47
47
  packageManager,
48
48
  aventaraVersion: io.aventaraVersion,
49
49
  skipInstall: command.skipInstall,
50
50
  read,
51
- ...(prepared.wiringByHand === undefined
52
- ? {}
53
- : { wiringByHand: prepared.wiringByHand }),
54
51
  warnings: prepared.warnings,
55
52
  });
56
53
  await confirmConflicts(plan.conflicts, {
@@ -105,16 +102,29 @@ async function prepareFresh(command, io, inspection, detectedPackageManager) {
105
102
  if (entry === undefined || db === undefined || packageManager === undefined) {
106
103
  throw new Error("the init questions settled without an ORM, database and package manager");
107
104
  }
105
+ const { hosts } = templatesFor(entry);
108
106
  return {
109
107
  entry,
110
108
  provider: db,
111
109
  packageManager,
112
- scaffold: templatesFor(entry).scaffold({
113
- entry,
114
- provider: db,
115
- moduleKind: inspection.moduleKind,
116
- packageManager,
117
- }),
110
+ hosted: inspection.host.alias === "nest"
111
+ ? {
112
+ host: "nest",
113
+ scaffold: hosts.nest.scaffold({
114
+ entry,
115
+ provider: db,
116
+ moduleKind: inspection.moduleKind,
117
+ packageManager,
118
+ }),
119
+ }
120
+ : {
121
+ host: "next",
122
+ scaffold: hosts.next.scaffold({
123
+ entry,
124
+ provider: db,
125
+ packageManager,
126
+ }),
127
+ },
118
128
  warnings: [],
119
129
  };
120
130
  }
@@ -134,6 +144,10 @@ function installedVersion(directory, name) {
134
144
  }
135
145
  }
136
146
  async function prepareReuse(command, io, inspection, detectedPackageManager) {
147
+ if (inspection.host.alias !== "nest") {
148
+ const orm = inspection.orm;
149
+ throw new AvProjectRefusedError(`this ${inspection.host.label} project already uses ${orm.families.join(" and ")} (${orm.packages.join(", ")}), and aventara init sets an ORM up in a ${inspection.host.label} project that has none; it reuses an existing setup in a NestJS project only`);
150
+ }
137
151
  const dependencies = [
138
152
  ...Object.keys(inspection.manifest.dependencies ?? {}),
139
153
  ...Object.keys(inspection.manifest.devDependencies ?? {}),
@@ -176,9 +190,9 @@ async function prepareReuse(command, io, inspection, detectedPackageManager) {
176
190
  services: {
177
191
  scan,
178
192
  clientClass: templates.clientShape(facts).clientClass,
179
- newService: templates.newService.service,
180
- newModule: templates.newService.module,
181
- newServiceFileExists: existsSync(path.join(io.directory, templates.newService.service.file)),
193
+ newService: templates.hosts.nest.newService.service,
194
+ newModule: templates.hosts.nest.newService.module,
195
+ newServiceFileExists: existsSync(path.join(io.directory, templates.hosts.nest.newService.service.file)),
182
196
  label: entry.orm.label,
183
197
  },
184
198
  },
@@ -209,21 +223,24 @@ async function prepareReuse(command, io, inspection, detectedPackageManager) {
209
223
  entry,
210
224
  provider,
211
225
  packageManager,
212
- scaffold: templates.reuse({
213
- entry,
214
- facts: { ...facts, provider, clientOutput: facts.clientOutput },
215
- moduleKind: inspection.moduleKind,
216
- packageManager,
217
- service,
218
- module,
219
- writeService: service.isNew,
220
- artifact,
221
- }),
222
- ...(service.clientMember === undefined
223
- ? {}
224
- : {
225
- wiringByHand: `${service.exportName} holds its ${entry.orm.label} client instead of extending it`,
226
+ hosted: {
227
+ host: "nest",
228
+ scaffold: templates.hosts.nest.reuse({
229
+ entry,
230
+ facts: { ...facts, provider, clientOutput: facts.clientOutput },
231
+ moduleKind: inspection.moduleKind,
232
+ packageManager,
233
+ service,
234
+ module,
235
+ writeService: service.isNew,
236
+ artifact,
226
237
  }),
238
+ ...(service.clientMember === undefined
239
+ ? {}
240
+ : {
241
+ wiringByHand: `${service.exportName} holds its ${entry.orm.label} client instead of extending it`,
242
+ }),
243
+ },
227
244
  warnings,
228
245
  };
229
246
  }