servcraft 0.4.8 → 0.4.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.
package/ROADMAP.md CHANGED
@@ -4,7 +4,8 @@ This document outlines the planned features and improvements for Servcraft.
4
4
 
5
5
  ## Version History
6
6
 
7
- - **v0.4.8** (Current) - Automatic module installation during init - Phase 3 in progress 🚧
7
+ - **v0.4.9** (Current) - Flexible JS module system (ESM/CommonJS + .js/.cjs) - Phase 3 in progress 🚧
8
+ - **v0.4.8** - Automatic module installation during init
8
9
  - **v0.4.7** - CLI header alignment fix
9
10
  - **v0.4.6** - Improved CLI design
10
11
  - **v0.4.5** - Lazy Prisma client initialization
@@ -1949,7 +1949,7 @@ async function findServercraftModules() {
1949
1949
  }
1950
1950
  return null;
1951
1951
  }
1952
- async function generateModuleFiles(moduleName, moduleDir) {
1952
+ async function generateModuleFiles(moduleName, moduleDir, language = "typescript", moduleSystem = "esm", fileExtension = "ts") {
1953
1953
  const moduleNameMap = {
1954
1954
  users: "user",
1955
1955
  "rate-limit": "rate-limit",
@@ -1962,7 +1962,8 @@ async function generateModuleFiles(moduleName, moduleDir) {
1962
1962
  if (servercraftModulesDir) {
1963
1963
  const sourceModuleDir = import_path3.default.join(servercraftModulesDir, sourceDirName);
1964
1964
  if (await fileExists(sourceModuleDir)) {
1965
- await copyModuleFromSource(sourceModuleDir, moduleDir);
1965
+ const jsExt = language === "javascript" ? fileExtension : "js";
1966
+ await copyModuleFromSource(sourceModuleDir, moduleDir, language, moduleSystem, jsExt);
1966
1967
  return;
1967
1968
  }
1968
1969
  }
@@ -1989,16 +1990,48 @@ async function generateModuleFiles(moduleName, moduleDir) {
1989
1990
  await generateGenericModule(moduleDir, moduleName);
1990
1991
  }
1991
1992
  }
1992
- async function copyModuleFromSource(sourceDir, targetDir) {
1993
+ function convertESMtoCommonJS(content) {
1994
+ return content.replace(/^export\s+class\s+/gm, "class ").replace(/^export\s+function\s+/gm, "function ").replace(/^export\s+const\s+/gm, "const ").replace(/^export\s+let\s+/gm, "let ").replace(/^export\s+var\s+/gm, "var ").replace(
1995
+ /import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]([^'"]+)['"]/g,
1996
+ "const { $1 } = require('$2')"
1997
+ ).replace(/import\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g, "const $1 = require('$2')").replace(
1998
+ /import\s+(\w+)\s*,\s*\{\s*([^}]+)\s*\}\s*from\s*['"]([^'"]+)['"]/g,
1999
+ "const $1 = require('$3');\nconst { $2 } = require('$3')"
2000
+ ).replace(/^(class\s+(\w+)\s+\{[\s\S]*?\n\})/gm, "$1\nmodule.exports.$2 = $2;").replace(/export\s*\{\s*([^}]+)\s*\}/g, (match, exports2) => {
2001
+ const items = exports2.split(",").map((item) => item.trim()).filter(Boolean);
2002
+ return items.map((item) => `module.exports.${item} = ${item};`).join("\n");
2003
+ });
2004
+ }
2005
+ async function copyModuleFromSource(sourceDir, targetDir, language = "typescript", moduleSystem = "esm", fileExtension = "js") {
1993
2006
  const entries = await fs4.readdir(sourceDir, { withFileTypes: true });
1994
2007
  for (const entry of entries) {
1995
2008
  const sourcePath = import_path3.default.join(sourceDir, entry.name);
1996
- const targetPath = import_path3.default.join(targetDir, entry.name);
2009
+ let targetPath = import_path3.default.join(targetDir, entry.name);
1997
2010
  if (entry.isDirectory()) {
1998
2011
  await fs4.mkdir(targetPath, { recursive: true });
1999
- await copyModuleFromSource(sourcePath, targetPath);
2012
+ await copyModuleFromSource(sourcePath, targetPath, language, moduleSystem, fileExtension);
2000
2013
  } else {
2001
- await fs4.copyFile(sourcePath, targetPath);
2014
+ if (language === "javascript" && entry.name.endsWith(".ts")) {
2015
+ const ext = `.${fileExtension}`;
2016
+ targetPath = targetPath.replace(/\.ts$/, ext);
2017
+ let content = await fs4.readFile(sourcePath, "utf-8");
2018
+ for (let i = 0; i < 3; i++) {
2019
+ content = content.replace(/import\s+type\s+\{[^}]+\}\s+from\s+['"][^'"]+['"];?\s*\n/g, "").replace(/import\s+\{([^}]+)\}\s+from/g, (match, imports) => {
2020
+ const filtered = imports.split(",").map((imp) => imp.trim()).filter((imp) => !imp.startsWith("type ")).join(", ");
2021
+ return filtered ? `import {${filtered}} from` : "";
2022
+ }).replace(/from\s+['"](.+?)\.js['"]/g, `from '$1${ext}'`).replace(/\b(private|public|protected|readonly)\s+/g, "").replace(
2023
+ /:\s*[A-Z]\w+(<[^>]+>)?(\[\])?(\s*[|&]\s*[A-Z]\w+(<[^>]+>)?(\[\])?)*(?=[,)\s=\n])/g,
2024
+ ""
2025
+ ).replace(/(\w+)\s*:\s*[^,)=\n]+([,)])/g, "$1$2").replace(/(\w+)\s*:\s*[^=\n{]+(\s*=)/g, "$1$2").replace(/\)\s*:\s*[^{=\n]+\s*([{=])/g, ") $1").replace(/^export\s+(interface|type)\s+[^;]+;?\s*$/gm, "").replace(/^(interface|type)\s+[^;]+;?\s*$/gm, "").replace(/\s+as\s+\w+/g, "").replace(/<[A-Z][\w,\s<>[\]|&]*>/g, "");
2026
+ }
2027
+ content = content.replace(/^\s*\n/gm, "");
2028
+ if (moduleSystem === "commonjs") {
2029
+ content = convertESMtoCommonJS(content);
2030
+ }
2031
+ await fs4.writeFile(targetPath, content, "utf-8");
2032
+ } else {
2033
+ await fs4.copyFile(sourcePath, targetPath);
2034
+ }
2002
2035
  }
2003
2036
  }
2004
2037
  }
@@ -2139,13 +2172,13 @@ var initCommand = new import_commander2.Command("init").alias("new").description
2139
2172
  console.log(import_chalk6.default.cyan("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
2140
2173
  console.log(import_chalk6.default.cyan("\u2502") + " " + import_chalk6.default.cyan("\u2502"));
2141
2174
  console.log(
2142
- import_chalk6.default.cyan("\u2502") + " " + import_chalk6.default.bold.white("\u{1F680} Servcraft") + import_chalk6.default.gray(" - Project Generator") + " " + import_chalk6.default.cyan("\u2502")
2175
+ import_chalk6.default.cyan("\u2502") + import_chalk6.default.bold.white("\u{1F680} Servcraft") + import_chalk6.default.gray(" - Project Generator") + " " + import_chalk6.default.cyan("\u2502")
2143
2176
  );
2144
2177
  console.log(
2145
- import_chalk6.default.cyan("\u2502") + " " + import_chalk6.default.gray("by ") + import_chalk6.default.blue("Yao Logan") + import_chalk6.default.gray(" (@Le-Sourcier)") + " " + import_chalk6.default.cyan("\u2502")
2178
+ import_chalk6.default.cyan("\u2502") + " " + import_chalk6.default.gray("by ") + import_chalk6.default.blue("Yao Logan") + import_chalk6.default.gray(" (@Le-Sourcier)") + " " + import_chalk6.default.cyan("\u2502")
2146
2179
  );
2147
2180
  console.log(
2148
- import_chalk6.default.cyan("\u2502") + " " + import_chalk6.default.bgBlue.white(" in/yao-logan ") + " " + import_chalk6.default.cyan("\u2502")
2181
+ import_chalk6.default.cyan("\u2502") + " " + import_chalk6.default.bgBlue.white(" in/yao-logan ") + " " + import_chalk6.default.cyan("\u2502")
2149
2182
  );
2150
2183
  console.log(import_chalk6.default.cyan("\u2502") + " " + import_chalk6.default.cyan("\u2502"));
2151
2184
  console.log(import_chalk6.default.cyan("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
@@ -2153,17 +2186,24 @@ var initCommand = new import_commander2.Command("init").alias("new").description
2153
2186
  let options;
2154
2187
  if (cmdOptions?.yes) {
2155
2188
  const db = cmdOptions.db || "postgresql";
2189
+ const language = cmdOptions.javascript ? "javascript" : "typescript";
2190
+ const moduleSystem = cmdOptions.commonjs ? "commonjs" : "esm";
2191
+ let fileExtension = "ts";
2192
+ if (language === "javascript") {
2193
+ fileExtension = moduleSystem === "commonjs" ? "cjs" : "js";
2194
+ }
2156
2195
  options = {
2157
2196
  name: name || "my-servcraft-app",
2158
- language: cmdOptions.javascript ? "javascript" : "typescript",
2159
- moduleSystem: cmdOptions.commonjs ? "commonjs" : "esm",
2197
+ language,
2198
+ moduleSystem,
2199
+ fileExtension,
2160
2200
  database: db,
2161
2201
  orm: db === "mongodb" ? "mongoose" : db === "none" ? "none" : "prisma",
2162
2202
  validator: "zod",
2163
2203
  features: ["auth", "users", "email"]
2164
2204
  };
2165
2205
  } else {
2166
- const answers = await import_inquirer2.default.prompt([
2206
+ const basicAnswers = await import_inquirer2.default.prompt([
2167
2207
  {
2168
2208
  type: "input",
2169
2209
  name: "name",
@@ -2185,17 +2225,43 @@ var initCommand = new import_commander2.Command("init").alias("new").description
2185
2225
  { name: " JavaScript", value: "javascript" }
2186
2226
  ],
2187
2227
  default: "typescript"
2188
- },
2189
- {
2190
- type: "list",
2191
- name: "moduleSystem",
2192
- message: "\u{1F4E6} Select module system:",
2193
- choices: [
2194
- { name: "\u2728 ESM (import/export) - Recommended", value: "esm" },
2195
- { name: " CommonJS (require/module.exports)", value: "commonjs" }
2196
- ],
2197
- default: "esm"
2198
- },
2228
+ }
2229
+ ]);
2230
+ let moduleSystem = "esm";
2231
+ let fileExtension = "ts";
2232
+ if (basicAnswers.language === "javascript") {
2233
+ const jsConfigAnswers = await import_inquirer2.default.prompt([
2234
+ {
2235
+ type: "list",
2236
+ name: "moduleSystem",
2237
+ message: "\u{1F4E6} Select module syntax:",
2238
+ choices: [
2239
+ { name: "\u2728 ESM (import/export)", value: "esm" },
2240
+ { name: " CommonJS (require/module.exports)", value: "commonjs" }
2241
+ ],
2242
+ default: "esm"
2243
+ },
2244
+ {
2245
+ type: "list",
2246
+ name: "fileExtension",
2247
+ message: "\u{1F4C4} Select file extension:",
2248
+ choices: (answers2) => {
2249
+ if (answers2.moduleSystem === "esm") {
2250
+ return [{ name: "\u2728 .js (standard for ESM)", value: "js" }];
2251
+ } else {
2252
+ return [
2253
+ { name: "\u2728 .cjs (explicit CommonJS)", value: "cjs" },
2254
+ { name: " .js (CommonJS in .js)", value: "js" }
2255
+ ];
2256
+ }
2257
+ },
2258
+ default: (answers2) => answers2.moduleSystem === "esm" ? "js" : "cjs"
2259
+ }
2260
+ ]);
2261
+ moduleSystem = jsConfigAnswers.moduleSystem;
2262
+ fileExtension = jsConfigAnswers.fileExtension;
2263
+ }
2264
+ const restAnswers = await import_inquirer2.default.prompt([
2199
2265
  {
2200
2266
  type: "list",
2201
2267
  name: "database",
@@ -2234,6 +2300,12 @@ var initCommand = new import_commander2.Command("init").alias("new").description
2234
2300
  ]
2235
2301
  }
2236
2302
  ]);
2303
+ const answers = {
2304
+ ...basicAnswers,
2305
+ moduleSystem,
2306
+ fileExtension,
2307
+ ...restAnswers
2308
+ };
2237
2309
  const db = answers.database;
2238
2310
  options = {
2239
2311
  ...answers,
@@ -2339,7 +2411,13 @@ var initCommand = new import_commander2.Command("init").alias("new").description
2339
2411
  moduleSpinner.text = `Installing ${feature} module...`;
2340
2412
  const moduleDir = import_path4.default.join(projectDir, "src/modules", feature);
2341
2413
  await ensureDir(moduleDir);
2342
- await generateModuleFiles(feature, moduleDir);
2414
+ await generateModuleFiles(
2415
+ feature,
2416
+ moduleDir,
2417
+ options.language,
2418
+ options.moduleSystem,
2419
+ options.fileExtension
2420
+ );
2343
2421
  }
2344
2422
  moduleSpinner.succeed(`${options.features.length} module(s) installed!`);
2345
2423
  } catch (err) {