Skip to content

Commit 6d5030a

Browse files
Merge pull request #112 from afmireski/feature-add-tests-for-qse-init-with-docker-compose-using-cache-and-db
Add tests for qse init command with docker-compose flag, using cache service and db enabled
2 parents 492d093 + f88eee8 commit 6d5030a

8 files changed

Lines changed: 410 additions & 841 deletions

File tree

.mise.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[tools]
2+
node = "22.13"

bin/configs.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export const metadata = {
22
command: "qse",
33
name: "Quick Start Express",
4-
version: "v2.0.0",
4+
version: "v2.0.1",
55
description:
66
"A simple CLI tool to generate Express servers from multiple available templates.",
77
oneLineDescription:

bin/index.js

Lines changed: 1 addition & 262 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,16 @@
11
#!/usr/bin/env node
22

33
import { Option, program } from "commander";
4-
import fs from "fs-extra";
5-
import path from "path";
6-
import { fileURLToPath } from "url";
7-
import { execSync } from "child_process";
84
import figlet from "figlet";
95
import chalk from "chalk";
10-
import { createSpinner } from "nanospinner";
116
import {
127
metadata,
138
commands,
149
templates,
1510
supportedDockerComposeCacheImages,
1611
} from "./configs.js";
17-
import validate from "validate-npm-package-name";
18-
import {
19-
getServicesData,
20-
generateDockerComposeFile,
21-
userPrompts,
22-
} from "./util/docker.js";
23-
import { initMenu } from "./util/menu.js";
2412
import { clearCWD } from "./util/clear.js";
25-
26-
const __filename = fileURLToPath(import.meta.url);
27-
const __dirname = path.dirname(__filename);
28-
const parentDir = path.dirname(__dirname);
13+
import { initCommand } from "./init.js";
2914

3015
program
3116
.name(metadata.command)
@@ -113,252 +98,6 @@ program
11398
clearCWD();
11499
});
115100

116-
async function initCommand(options) {
117-
const selectedTemplate = options.template || "basic"; // Default to 'basic' if no template is specified
118-
const packageName = options.name || "qse-server"; // Default to 'qse-server' if no name is specified
119-
const removeNodemon = options.removeNodemon;
120-
const removeDependencies = options.removeDeps;
121-
122-
if (!options.template) {
123-
initMenu(initCommand, options);
124-
return;
125-
}
126-
127-
// Docker Compose options.
128-
const dockerCompose = options.dockerCompose;
129-
const cacheService = options.cacheService;
130-
const skipDb = options.skipDb || false;
131-
132-
if (packageName) {
133-
const validateResult = validate(packageName);
134-
if (validateResult.validForNewPackages === false) {
135-
const errors = validateResult.errors || validateResult.warnings;
136-
console.error(
137-
chalk.red.bold(
138-
`Invalid package name: ${errors.join(
139-
", ",
140-
)}. Please provide a valid package name.`,
141-
),
142-
);
143-
return;
144-
}
145-
}
146-
147-
if (!templates[selectedTemplate]) {
148-
console.error(
149-
chalk.red(
150-
`Template ${chalk.bgRed.bold(
151-
selectedTemplate,
152-
)} does not exist. To see available templates use ${chalk.yellow(
153-
'"qse list"',
154-
)}.`,
155-
),
156-
);
157-
return;
158-
}
159-
160-
const targetDir = process.cwd();
161-
const templatePath = path.join(
162-
parentDir,
163-
"templates",
164-
templates[selectedTemplate].name,
165-
);
166-
167-
const isUrl = templates[selectedTemplate].isUrl;
168-
const needDB = templates[selectedTemplate].needDB && !skipDb;
169-
170-
let dockerTemplate =
171-
selectedTemplate.split("_")[0] === "express" ||
172-
selectedTemplate.split("_")[0] === "basic"
173-
? "express"
174-
: selectedTemplate.split("_")[0];
175-
176-
const dockerTemplatePath = path.join(
177-
parentDir,
178-
"templates",
179-
"Docker",
180-
dockerTemplate,
181-
"Dockerfile",
182-
);
183-
184-
const destinationPath = path.join(targetDir);
185-
const dockerFileDestination = path.join(destinationPath, "Dockerfile");
186-
187-
let runtimeNeedDB = false;
188-
if (dockerCompose) {
189-
try {
190-
console.log();
191-
const userPrompt = await userPrompts(needDB, cacheService);
192-
runtimeNeedDB = userPrompt.runtimeNeedDB;
193-
194-
const serviceData = await getServicesData(
195-
packageName,
196-
selectedTemplate,
197-
runtimeNeedDB,
198-
userPrompt.addCacheService,
199-
cacheService,
200-
);
201-
202-
console.log("Starting server initialization...");
203-
204-
const dockerSpinner = createSpinner(
205-
`Creating Docker Compose File with Entered Services...`,
206-
).start();
207-
208-
const composeFileContent = generateDockerComposeFile(
209-
runtimeNeedDB,
210-
serviceData,
211-
packageName,
212-
selectedTemplate,
213-
);
214-
const composeFilePath = path.join(targetDir, "docker-compose.yml");
215-
216-
fs.writeFileSync(composeFilePath, composeFileContent);
217-
dockerSpinner.success({
218-
text: `Docker Compose file generated successfully.`,
219-
});
220-
} catch (error) {
221-
console.log(chalk.red("Error generating Docker Compose file"));
222-
console.error(error.message);
223-
return;
224-
}
225-
} else {
226-
console.log();
227-
console.log("Starting server initialization...");
228-
}
229-
230-
const copySpinner = createSpinner("Creating server files...").start();
231-
try {
232-
await fs.copy(templatePath, destinationPath);
233-
if (dockerCompose) {
234-
try {
235-
await fs.copyFile(dockerTemplatePath, dockerFileDestination);
236-
} catch (error) {
237-
copySpinner.error({ text: "Error creating Dockerfile.\n" });
238-
console.error(error.message);
239-
}
240-
}
241-
242-
copySpinner.success({ text: "Created server files successfully." });
243-
244-
if (removeNodemon) {
245-
const nodemonSpinner = createSpinner("Removing nodemon...").start();
246-
try {
247-
const packageJsonPath = path.join(
248-
destinationPath,
249-
"package.json",
250-
);
251-
const packageJsonContent = fs.readFileSync(
252-
packageJsonPath,
253-
"utf8",
254-
);
255-
const packageJson = JSON.parse(packageJsonContent);
256-
257-
if (
258-
packageJson.devDependencies &&
259-
packageJson.devDependencies.nodemon
260-
) {
261-
delete packageJson.devDependencies.nodemon;
262-
if (!Object.keys(packageJson.devDependencies).length) {
263-
delete packageJson.devDependencies;
264-
}
265-
}
266-
if (packageJson.scripts && packageJson.scripts.dev) {
267-
delete packageJson.scripts.dev;
268-
}
269-
270-
fs.writeFileSync(
271-
packageJsonPath,
272-
JSON.stringify(packageJson, null, 2),
273-
);
274-
275-
nodemonSpinner.success({
276-
text: "Removed nodemon successfully.",
277-
});
278-
} catch (err) {
279-
nodemonSpinner.error({ text: "Error removing nodemon.\n" });
280-
console.error(err.message);
281-
}
282-
}
283-
} catch (err) {
284-
copySpinner.error({ text: "Error creating server files.\n" });
285-
console.error(err.message);
286-
}
287-
288-
const addNameAndTypeSpinner = createSpinner(
289-
"Adding name and type declaration...",
290-
).start();
291-
try {
292-
const packageJsonPath = path.join(targetDir, "package.json");
293-
const packageJsonContent = fs.readFileSync(packageJsonPath, "utf8");
294-
const packageJson = JSON.parse(packageJsonContent);
295-
packageJson.name = packageName; // Set custom package name
296-
packageJson.type = "module"; // Define type as module
297-
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
298-
299-
addNameAndTypeSpinner.success({
300-
text: "Added name and type declaration successfully.",
301-
});
302-
} catch (err) {
303-
addNameAndTypeSpinner.error({
304-
text: "Error adding type declaration.\n",
305-
});
306-
console.error(err.message);
307-
}
308-
309-
if (!removeDependencies) {
310-
const installDependencies = createSpinner(
311-
"Installing dependency packages...",
312-
).start();
313-
try {
314-
execSync("npm i", { stdio: "ignore", cwd: targetDir });
315-
316-
installDependencies.success({
317-
text: "Installed dependencies successfully.",
318-
});
319-
} catch (err) {
320-
installDependencies.error({
321-
text: "Error installing dependencies.\n",
322-
});
323-
console.error(err);
324-
}
325-
}
326-
327-
console.log(chalk.green.bold("\nSetup complete! To run your server:"));
328-
if (removeDependencies) {
329-
console.log(
330-
chalk.yellow("Install dependencies: "),
331-
chalk.white.bold("npm i"),
332-
);
333-
}
334-
console.log(chalk.yellow("Run:"), chalk.white.bold("npm start"));
335-
if (!removeNodemon) {
336-
console.log(
337-
chalk.yellow("Run with hot reloading:"),
338-
chalk.white.bold("npm run dev"),
339-
);
340-
}
341-
if (dockerCompose) {
342-
console.log(
343-
chalk.yellow("To start your services with Docker Compose:"),
344-
chalk.white.bold("docker compose up -d"),
345-
);
346-
}
347-
348-
if (dockerCompose && isUrl === true && runtimeNeedDB === true) {
349-
console.log(
350-
chalk.yellow("Important Note:"),
351-
chalk.white("Use"),
352-
chalk.blueBright.bold("host.docker.internal"),
353-
chalk.white("instead of"),
354-
chalk.blueBright.bold("localhost/127.0.0.1"),
355-
chalk.white("in your Database Connection URL in the"),
356-
chalk.blueBright.bold(".env"),
357-
chalk.white("file for Docker to work correctly."),
358-
);
359-
}
360-
}
361-
362101
const toolIntro = () => {
363102
console.log(
364103
figlet.textSync(metadata.name, {

0 commit comments

Comments
 (0)