create-nextly-app 0.0.2-alpha.5 → 0.0.2-alpha.6

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.
@@ -18003,9 +18003,6 @@ var import_fs_extra = __toESM(require_lib(), 1);
18003
18003
  var ADMIN_PAGE_TEMPLATE = `"use client";
18004
18004
 
18005
18005
  import "@nextlyhq/admin/style.css";
18006
- import "@nextlyhq/plugin-form-builder/admin";
18007
- import "@nextlyhq/plugin-form-builder/styles/builder.css";
18008
- import "@nextlyhq/plugin-form-builder/styles/submissions-filter.css";
18009
18006
  import { RootLayout, QueryProvider, ErrorBoundary } from "@nextlyhq/admin";
18010
18007
 
18011
18008
  export default function AdminPage() {
@@ -24856,6 +24853,316 @@ createExeca(mapNode);
24856
24853
  createExeca(mapScriptAsync, {}, deepScriptOptions, setScriptSync);
24857
24854
  getIpcExport();
24858
24855
 
24856
+ // src/utils/template.ts
24857
+ var import_fs_extra8 = __toESM(require_lib(), 1);
24858
+ var PROJECT_TYPES_WITH_FORM_BUILDER = /* @__PURE__ */ new Set([
24859
+ "blog"
24860
+ ]);
24861
+ function projectUsesFormBuilder(projectType) {
24862
+ return PROJECT_TYPES_WITH_FORM_BUILDER.has(projectType);
24863
+ }
24864
+ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
24865
+ ".ts",
24866
+ ".tsx",
24867
+ ".js",
24868
+ ".jsx",
24869
+ ".json",
24870
+ ".env",
24871
+ ".md",
24872
+ ".css",
24873
+ ".html",
24874
+ ".mjs",
24875
+ ".cjs"
24876
+ ]);
24877
+ var SKIP_FILES = /* @__PURE__ */ new Set([".DS_Store", "Thumbs.db", ".gitkeep"]);
24878
+ function resolveTemplatePath(localTemplatePath) {
24879
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
24880
+ const fromDist = path.resolve(__dirname, "../templates");
24881
+ if (import_fs_extra8.default.existsSync(fromDist)) {
24882
+ return fromDist;
24883
+ }
24884
+ const fromSrc = path.resolve(__dirname, "../../templates");
24885
+ if (import_fs_extra8.default.existsSync(fromSrc)) {
24886
+ return fromSrc;
24887
+ }
24888
+ throw new Error(
24889
+ "Could not find templates directory. Use --local-template to specify the templates path, or ensure templates are bundled."
24890
+ );
24891
+ }
24892
+ function buildPlaceholderMap(options) {
24893
+ const { database, databaseUrl } = options;
24894
+ return {
24895
+ "{{databaseDialect}}": database.type,
24896
+ "{{databaseUrl}}": databaseUrl || database.envExample
24897
+ };
24898
+ }
24899
+ async function replacePlaceholdersInFile(filePath, placeholders) {
24900
+ const ext = path.extname(filePath).toLowerCase();
24901
+ const basename = path.basename(filePath);
24902
+ const isTextFile = TEXT_EXTENSIONS.has(ext) || basename.startsWith(".env") || basename === ".gitignore";
24903
+ if (!isTextFile) return;
24904
+ let content = await import_fs_extra8.default.readFile(filePath, "utf-8");
24905
+ let changed = false;
24906
+ for (const [placeholder, value] of Object.entries(placeholders)) {
24907
+ if (content.includes(placeholder)) {
24908
+ content = content.replaceAll(placeholder, value);
24909
+ changed = true;
24910
+ }
24911
+ }
24912
+ if (changed) {
24913
+ await import_fs_extra8.default.writeFile(filePath, content, "utf-8");
24914
+ }
24915
+ }
24916
+ async function replacePlaceholders(dir, placeholders) {
24917
+ const entries = await import_fs_extra8.default.readdir(dir, { withFileTypes: true });
24918
+ for (const entry of entries) {
24919
+ const fullPath = path.join(dir, entry.name);
24920
+ if (entry.isDirectory()) {
24921
+ if (entry.name === "node_modules" || entry.name === ".git") continue;
24922
+ await replacePlaceholders(fullPath, placeholders);
24923
+ } else if (entry.isFile()) {
24924
+ await replacePlaceholdersInFile(fullPath, placeholders);
24925
+ }
24926
+ }
24927
+ }
24928
+ var PINNED_VERSIONS = {
24929
+ // Next.js ecosystem — resolved at runtime via fetchLatestVersion()
24930
+ // (see generatePackageJson)
24931
+ react: "^19.1.0",
24932
+ "react-dom": "^19.1.0",
24933
+ // Dev dependencies
24934
+ typescript: "^5",
24935
+ "@types/node": "^20",
24936
+ "@types/react": "^19",
24937
+ "@types/react-dom": "^19",
24938
+ "@tailwindcss/postcss": "^4",
24939
+ tailwindcss: "^4",
24940
+ eslint: "^9"
24941
+ };
24942
+ var RUNTIME_RESOLVED_PACKAGES = ["next", "eslint-config-next"];
24943
+ var NEXTLY_PACKAGES = [
24944
+ "nextly",
24945
+ "@nextlyhq/admin",
24946
+ "@nextlyhq/adapter-drizzle",
24947
+ "@nextlyhq/adapter-postgres",
24948
+ "@nextlyhq/adapter-mysql",
24949
+ "@nextlyhq/adapter-sqlite"
24950
+ ];
24951
+ var resolvedNextlyVersions = null;
24952
+ async function fetchLatestVersion(pkg) {
24953
+ try {
24954
+ const res = await fetch(
24955
+ `https://registry.npmjs.org/-/package/${encodeURIComponent(pkg)}/dist-tags`,
24956
+ { signal: AbortSignal.timeout(5e3) }
24957
+ );
24958
+ if (!res.ok) return "latest";
24959
+ const data = await res.json();
24960
+ return data.latest ? `^${data.latest}` : "latest";
24961
+ } catch {
24962
+ return "latest";
24963
+ }
24964
+ }
24965
+ async function resolveNextlyVersions() {
24966
+ if (resolvedNextlyVersions) return resolvedNextlyVersions;
24967
+ const entries = await Promise.all(
24968
+ NEXTLY_PACKAGES.map(
24969
+ async (pkg) => [pkg, await fetchLatestVersion(pkg)]
24970
+ )
24971
+ );
24972
+ resolvedNextlyVersions = Object.fromEntries(entries);
24973
+ return resolvedNextlyVersions;
24974
+ }
24975
+ var resolvedRuntimeVersions = null;
24976
+ async function resolveRuntimeVersions() {
24977
+ if (resolvedRuntimeVersions) return resolvedRuntimeVersions;
24978
+ const FALLBACKS = {
24979
+ next: "^16.1.0",
24980
+ "eslint-config-next": "^16.1.0"
24981
+ };
24982
+ const entries = await Promise.all(
24983
+ RUNTIME_RESOLVED_PACKAGES.map(async (pkg) => {
24984
+ const version2 = await fetchLatestVersion(pkg);
24985
+ return [pkg, version2 === "latest" ? FALLBACKS[pkg] : version2];
24986
+ })
24987
+ );
24988
+ resolvedRuntimeVersions = Object.fromEntries(entries);
24989
+ return resolvedRuntimeVersions;
24990
+ }
24991
+ async function generatePackageJson(projectName, database, useYalc = false, projectType = "blank") {
24992
+ const runtimeVersions = await resolveRuntimeVersions();
24993
+ const dependencies = {
24994
+ next: runtimeVersions.next,
24995
+ react: PINNED_VERSIONS.react,
24996
+ "react-dom": PINNED_VERSIONS["react-dom"]
24997
+ };
24998
+ dependencies["@tanstack/react-query"] = "^5.62.0";
24999
+ if (!useYalc) {
25000
+ const versions = await resolveNextlyVersions();
25001
+ dependencies["nextly"] = versions["nextly"];
25002
+ dependencies["@nextlyhq/admin"] = versions["@nextlyhq/admin"];
25003
+ dependencies["@nextlyhq/ui"] = versions["@nextlyhq/ui"] || "latest";
25004
+ dependencies["@nextlyhq/adapter-drizzle"] = versions["@nextlyhq/adapter-drizzle"];
25005
+ dependencies[database.adapter] = versions[database.adapter] || "latest";
25006
+ if (projectUsesFormBuilder(projectType)) {
25007
+ dependencies["@nextlyhq/plugin-form-builder"] = versions["@nextlyhq/plugin-form-builder"] || "latest";
25008
+ }
25009
+ }
25010
+ const devDependencies = {
25011
+ typescript: PINNED_VERSIONS.typescript,
25012
+ "@types/node": PINNED_VERSIONS["@types/node"],
25013
+ "@types/react": PINNED_VERSIONS["@types/react"],
25014
+ "@types/react-dom": PINNED_VERSIONS["@types/react-dom"],
25015
+ "@tailwindcss/postcss": PINNED_VERSIONS["@tailwindcss/postcss"],
25016
+ tailwindcss: PINNED_VERSIONS.tailwindcss,
25017
+ eslint: PINNED_VERSIONS.eslint,
25018
+ "eslint-config-next": runtimeVersions["eslint-config-next"],
25019
+ // Pagefind powers /search in the blog template. Zero-config
25020
+ // static index generated at `next build` time. Templates that
25021
+ // don't ship a /search page simply won't invoke it.
25022
+ pagefind: "^1.1.0"
25023
+ };
25024
+ const pkg = {
25025
+ name: projectName,
25026
+ version: "0.1.0",
25027
+ private: true,
25028
+ scripts: {
25029
+ // F1 PR 4: dev now boots Nextly in single-process mode via `next dev`.
25030
+ // The lazy drizzle-kit/api import (PR 1) plus the in-process HMR
25031
+ // listener (PR 2) replaced the wrapper that previously owned the
25032
+ // terminal, schema prompts, and child supervision. `nextly dev` is
25033
+ // gone; the only supported dev command is the standard `next dev`.
25034
+ dev: "next dev --turbopack",
25035
+ // Build: migrate DB + compile Next.js + (if present) generate
25036
+ // the Pagefind search index. Templates without the search
25037
+ // script silently skip the last step.
25038
+ build: "nextly migrate && next build && (test -f scripts/build-search-index.mjs && node scripts/build-search-index.mjs || true)",
25039
+ "search:index": "node scripts/build-search-index.mjs",
25040
+ start: "next start",
25041
+ lint: "next lint",
25042
+ nextly: "nextly",
25043
+ // First-time setup: sync schema + seed system permissions. Demo
25044
+ // content is seeded separately from the admin UI (visit /welcome
25045
+ // after running `pnpm dev` and completing /admin/setup).
25046
+ "db:setup": "nextly db:sync",
25047
+ "db:migrate": "nextly migrate",
25048
+ "db:migrate:status": "nextly migrate:status",
25049
+ "db:migrate:fresh": "nextly migrate:fresh",
25050
+ "db:migrate:reset": "nextly migrate:reset",
25051
+ "types:generate": "nextly generate:types"
25052
+ },
25053
+ dependencies,
25054
+ devDependencies
25055
+ };
25056
+ return JSON.stringify(pkg, null, 2) + "\n";
25057
+ }
25058
+ async function copyTemplate(options) {
25059
+ const {
25060
+ projectName,
25061
+ projectType,
25062
+ targetDir,
25063
+ database,
25064
+ databaseUrl,
25065
+ useYalc = false,
25066
+ approach,
25067
+ templateSource
25068
+ } = options;
25069
+ if (targetDir !== process.cwd() && await import_fs_extra8.default.pathExists(targetDir)) {
25070
+ throw new Error(
25071
+ `Directory "${path.basename(targetDir)}" already exists. Please choose a different name.`
25072
+ );
25073
+ }
25074
+ let baseDir;
25075
+ let typeDir;
25076
+ if (templateSource) {
25077
+ baseDir = templateSource.basePath;
25078
+ typeDir = templateSource.templatePath;
25079
+ } else {
25080
+ const templatesRoot = resolveTemplatePath();
25081
+ baseDir = path.join(templatesRoot, "base");
25082
+ typeDir = path.join(templatesRoot, projectType);
25083
+ }
25084
+ if (!await import_fs_extra8.default.pathExists(baseDir)) {
25085
+ throw new Error(
25086
+ `Base template not found at ${baseDir}. The package may be corrupted or the download failed.`
25087
+ );
25088
+ }
25089
+ if (!await import_fs_extra8.default.pathExists(typeDir)) {
25090
+ throw new Error(
25091
+ `Template "${projectType}" not found at ${typeDir}. Available templates: blank, blog.`
25092
+ );
25093
+ }
25094
+ await import_fs_extra8.default.copy(baseDir, targetDir, {
25095
+ filter: (_src) => {
25096
+ const basename = path.basename(_src);
25097
+ return !SKIP_FILES.has(basename);
25098
+ }
25099
+ });
25100
+ const templateSrcDir = path.join(typeDir, "src");
25101
+ if (await import_fs_extra8.default.pathExists(templateSrcDir)) {
25102
+ await import_fs_extra8.default.copy(templateSrcDir, path.join(targetDir, "src"), {
25103
+ overwrite: true,
25104
+ filter: (_src) => {
25105
+ const basename = path.basename(_src);
25106
+ return !SKIP_FILES.has(basename);
25107
+ }
25108
+ });
25109
+ }
25110
+ const templateRootConfig = path.join(typeDir, "nextly.config.ts");
25111
+ if (await import_fs_extra8.default.pathExists(templateRootConfig)) {
25112
+ await import_fs_extra8.default.copy(
25113
+ templateRootConfig,
25114
+ path.join(targetDir, "nextly.config.ts"),
25115
+ { overwrite: true }
25116
+ );
25117
+ }
25118
+ const configsDir = path.join(typeDir, "configs");
25119
+ if (approach && await import_fs_extra8.default.pathExists(configsDir)) {
25120
+ const configFileName = approach === "code-first" ? "codefirst.config.ts" : `${approach}.config.ts`;
25121
+ const configSrc = path.join(configsDir, configFileName);
25122
+ if (await import_fs_extra8.default.pathExists(configSrc)) {
25123
+ await import_fs_extra8.default.copy(configSrc, path.join(targetDir, "nextly.config.ts"), {
25124
+ overwrite: true
25125
+ });
25126
+ }
25127
+ const sharedSrc = path.join(configsDir, "shared.ts");
25128
+ if (await import_fs_extra8.default.pathExists(sharedSrc)) {
25129
+ await import_fs_extra8.default.copy(sharedSrc, path.join(targetDir, "shared.ts"), {
25130
+ overwrite: true
25131
+ });
25132
+ }
25133
+ }
25134
+ const frontendPagePath = path.join(
25135
+ targetDir,
25136
+ "src",
25137
+ "app",
25138
+ "(frontend)",
25139
+ "page.tsx"
25140
+ );
25141
+ const basePagePath = path.join(targetDir, "src", "app", "page.tsx");
25142
+ if (await import_fs_extra8.default.pathExists(frontendPagePath) && await import_fs_extra8.default.pathExists(basePagePath)) {
25143
+ await import_fs_extra8.default.remove(basePagePath);
25144
+ }
25145
+ const packageJsonContent = await generatePackageJson(
25146
+ projectName,
25147
+ database,
25148
+ useYalc,
25149
+ projectType
25150
+ );
25151
+ await import_fs_extra8.default.writeFile(
25152
+ path.join(targetDir, "package.json"),
25153
+ packageJsonContent,
25154
+ "utf-8"
25155
+ );
25156
+ if (database.type === "sqlite") {
25157
+ await import_fs_extra8.default.ensureDir(path.join(targetDir, "data"));
25158
+ }
25159
+ const placeholders = buildPlaceholderMap({ database, databaseUrl });
25160
+ if (approach) {
25161
+ placeholders["{{approach}}"] = approach;
25162
+ }
25163
+ await replacePlaceholders(targetDir, placeholders);
25164
+ }
25165
+
24859
25166
  // src/installers/dependencies.ts
24860
25167
  var INSTALL_COMMANDS = {
24861
25168
  npm: ["npm", "install"],
@@ -24875,14 +25182,16 @@ var ALL_ADAPTER_PACKAGES = [
24875
25182
  "@nextlyhq/adapter-mysql",
24876
25183
  "@nextlyhq/adapter-sqlite"
24877
25184
  ];
24878
- var TEMPLATE_PLUGIN_PACKAGES = [
24879
- "@nextlyhq/plugin-form-builder"
24880
- ];
25185
+ var TEMPLATE_PLUGIN_PACKAGES = ["@nextlyhq/plugin-form-builder"];
25186
+ function templatePluginPackages(projectType) {
25187
+ return projectType && projectUsesFormBuilder(projectType) ? TEMPLATE_PLUGIN_PACKAGES : [];
25188
+ }
24881
25189
  function getPackagesToInstall(database) {
24882
25190
  return [...CORE_PACKAGES, database.adapter];
24883
25191
  }
24884
- async function installDependencies(cwd, projectInfo, database, useYalc = false, isFreshProject = false) {
25192
+ async function installDependencies(cwd, projectInfo, database, useYalc = false, isFreshProject = false, projectType) {
24885
25193
  const pm = projectInfo.packageManager;
25194
+ const pluginPackages = templatePluginPackages(projectType);
24886
25195
  if (isFreshProject) {
24887
25196
  if (useYalc) {
24888
25197
  const yalcPackages = [
@@ -24892,7 +25201,7 @@ async function installDependencies(cwd, projectInfo, database, useYalc = false,
24892
25201
  "@nextlyhq/ui",
24893
25202
  "@nextlyhq/adapter-drizzle",
24894
25203
  ...ALL_ADAPTER_PACKAGES,
24895
- ...TEMPLATE_PLUGIN_PACKAGES
25204
+ ...pluginPackages
24896
25205
  ])
24897
25206
  ];
24898
25207
  await execa(pm, ["install"], { cwd });
@@ -24913,7 +25222,7 @@ async function installDependencies(cwd, projectInfo, database, useYalc = false,
24913
25222
  "@nextlyhq/ui",
24914
25223
  "@nextlyhq/adapter-drizzle",
24915
25224
  ...ALL_ADAPTER_PACKAGES,
24916
- ...TEMPLATE_PLUGIN_PACKAGES
25225
+ ...pluginPackages
24917
25226
  ])
24918
25227
  ];
24919
25228
  for (const pkg of yalcPackages) {
@@ -24928,7 +25237,7 @@ async function installDependencies(cwd, projectInfo, database, useYalc = false,
24928
25237
  }
24929
25238
 
24930
25239
  // src/lib/download-template.ts
24931
- var import_fs_extra8 = __toESM(require_lib(), 1);
25240
+ var import_fs_extra9 = __toESM(require_lib(), 1);
24932
25241
  var kr = Object.defineProperty;
24933
25242
  var vr = (s3, t2) => {
24934
25243
  for (var e in t2) kr(s3, e, { get: t2[e], enumerable: true });
@@ -27004,7 +27313,7 @@ var Vn = 512 * 1024;
27004
27313
  var $n = pr | ur | dr | mr;
27005
27314
  var lr = !fr && typeof ar == "number" ? ar | ur | dr | mr : null;
27006
27315
  var cs = lr !== null ? () => lr : Kn ? (s3) => s3 < Vn ? $n : "w" : () => "w";
27007
- var fs9 = (s3, t2, e) => {
27316
+ var fs10 = (s3, t2, e) => {
27008
27317
  try {
27009
27318
  return fs.lchownSync(s3, t2, e);
27010
27319
  } catch (i2) {
@@ -27044,7 +27353,7 @@ var ds = (s3, t2, e, i2) => {
27044
27353
  });
27045
27354
  };
27046
27355
  var qn = (s3, t2, e, i2) => {
27047
- t2.isDirectory() && us(path.resolve(s3, t2.name), e, i2), fs9(path.resolve(s3, t2.name), e, i2);
27356
+ t2.isDirectory() && us(path.resolve(s3, t2.name), e, i2), fs10(path.resolve(s3, t2.name), e, i2);
27048
27357
  };
27049
27358
  var us = (s3, t2, e) => {
27050
27359
  let i2;
@@ -27053,11 +27362,11 @@ var us = (s3, t2, e) => {
27053
27362
  } catch (r) {
27054
27363
  let n2 = r;
27055
27364
  if (n2?.code === "ENOENT") return;
27056
- if (n2?.code === "ENOTDIR" || n2?.code === "ENOTSUP") return fs9(s3, t2, e);
27365
+ if (n2?.code === "ENOTDIR" || n2?.code === "ENOTSUP") return fs10(s3, t2, e);
27057
27366
  throw n2;
27058
27367
  }
27059
27368
  for (let r of i2) qn(s3, r, t2, e);
27060
- return fs9(s3, t2, e);
27369
+ return fs10(s3, t2, e);
27061
27370
  };
27062
27371
  var we2 = class extends Error {
27063
27372
  path;
@@ -27802,7 +28111,7 @@ async function downloadTemplate(templateName, branch = "main") {
27802
28111
  process.env.TMPDIR || "/tmp",
27803
28112
  `nextly-template-${Date.now()}`
27804
28113
  );
27805
- await import_fs_extra8.default.ensureDir(tmpDir);
28114
+ await import_fs_extra9.default.ensureDir(tmpDir);
27806
28115
  const repoPrefix = `${GITHUB_REPO}-${branch}`;
27807
28116
  const baseFilter = `${repoPrefix}/templates/base/`;
27808
28117
  const templateFilter = `${repoPrefix}/templates/${templateName}/`;
@@ -27831,7 +28140,7 @@ async function downloadTemplate(templateName, branch = "main") {
27831
28140
  })
27832
28141
  );
27833
28142
  } catch (error) {
27834
- await import_fs_extra8.default.remove(tmpDir).catch(() => {
28143
+ await import_fs_extra9.default.remove(tmpDir).catch(() => {
27835
28144
  });
27836
28145
  if (error instanceof Error && error.name === "TimeoutError") {
27837
28146
  throw new Error(
@@ -27842,15 +28151,15 @@ async function downloadTemplate(templateName, branch = "main") {
27842
28151
  }
27843
28152
  const basePath = path.join(tmpDir, "templates", "base");
27844
28153
  const templatePath = path.join(tmpDir, "templates", templateName);
27845
- if (!await import_fs_extra8.default.pathExists(basePath)) {
27846
- await import_fs_extra8.default.remove(tmpDir).catch(() => {
28154
+ if (!await import_fs_extra9.default.pathExists(basePath)) {
28155
+ await import_fs_extra9.default.remove(tmpDir).catch(() => {
27847
28156
  });
27848
28157
  throw new Error(
27849
28158
  `Base template not found in downloaded archive. The branch "${branch}" may not contain templates.`
27850
28159
  );
27851
28160
  }
27852
- if (!await import_fs_extra8.default.pathExists(templatePath)) {
27853
- await import_fs_extra8.default.remove(tmpDir).catch(() => {
28161
+ if (!await import_fs_extra9.default.pathExists(templatePath)) {
28162
+ await import_fs_extra9.default.remove(tmpDir).catch(() => {
27854
28163
  });
27855
28164
  throw new Error(
27856
28165
  `Template "${templateName}" not found in downloaded archive. Available templates may differ on branch "${branch}".`
@@ -27861,12 +28170,12 @@ async function downloadTemplate(templateName, branch = "main") {
27861
28170
  async function resolveLocalTemplate(localPath, templateName) {
27862
28171
  const basePath = path.join(localPath, "base");
27863
28172
  const templatePath = path.join(localPath, templateName);
27864
- if (!await import_fs_extra8.default.pathExists(basePath)) {
28173
+ if (!await import_fs_extra9.default.pathExists(basePath)) {
27865
28174
  throw new Error(
27866
28175
  `Base template not found at ${basePath}. Check the --local-template path points to the templates/ directory.`
27867
28176
  );
27868
28177
  }
27869
- if (!await import_fs_extra8.default.pathExists(templatePath)) {
28178
+ if (!await import_fs_extra9.default.pathExists(templatePath)) {
27870
28179
  throw new Error(
27871
28180
  `Template "${templateName}" not found at ${templatePath}. Check the --local-template path points to the templates/ directory.`
27872
28181
  );
@@ -27884,7 +28193,7 @@ async function cleanupDownload(source) {
27884
28193
  const tmpBase = process.env.TMPDIR || "/tmp";
27885
28194
  if (source.basePath.startsWith(tmpBase)) {
27886
28195
  const extractionRoot = path.resolve(source.basePath, "../..");
27887
- await import_fs_extra8.default.remove(extractionRoot).catch(() => {
28196
+ await import_fs_extra9.default.remove(extractionRoot).catch(() => {
27888
28197
  });
27889
28198
  }
27890
28199
  }
@@ -27984,419 +28293,118 @@ var DATABASE_LABELS = {
27984
28293
  mysql: {
27985
28294
  label: "MySQL",
27986
28295
  hint: "Popular alternative for production"
27987
- }
27988
- };
27989
-
27990
- // src/prompts/project-name.ts
27991
- var import_fs_extra9 = __toESM(require_lib(), 1);
27992
- async function isExistingNextProject(cwd) {
27993
- const packageJsonPath = path.join(cwd, "package.json");
27994
- if (!await import_fs_extra9.default.pathExists(packageJsonPath)) return false;
27995
- try {
27996
- const packageJson = await import_fs_extra9.default.readJson(packageJsonPath);
27997
- const deps = {
27998
- ...packageJson.dependencies,
27999
- ...packageJson.devDependencies
28000
- };
28001
- return "next" in deps;
28002
- } catch {
28003
- return false;
28004
- }
28005
- }
28006
-
28007
- // src/prompts/template.ts
28008
- var import_picocolors2 = __toESM(require_picocolors2(), 1);
28009
- function getTemplatePromptOptions() {
28010
- return [
28011
- {
28012
- value: "blank",
28013
- label: "Blank project",
28014
- hint: "Start fresh with an empty config"
28015
- },
28016
- {
28017
- value: "blog",
28018
- label: "Blog",
28019
- hint: "Posts, authors, categories, clean design"
28020
- },
28021
- {
28022
- value: "_coming_soon",
28023
- label: import_picocolors2.default.dim("More templates coming soon"),
28024
- hint: "website, portfolio, e-commerce"
28025
- }
28026
- ];
28027
- }
28028
- function isValidTemplateSelection(value) {
28029
- return value !== "_coming_soon";
28030
- }
28031
-
28032
- // src/utils/detect.ts
28033
- var import_fs_extra10 = __toESM(require_lib(), 1);
28034
-
28035
- // src/utils/detect-pm-from-user-agent.ts
28036
- function detectPmFromUserAgent(userAgent) {
28037
- if (!userAgent) return null;
28038
- if (userAgent.startsWith("pnpm/")) return "pnpm";
28039
- if (userAgent.startsWith("yarn/")) return "yarn";
28040
- if (userAgent.startsWith("bun/")) return "bun";
28041
- if (userAgent.startsWith("npm/")) return "npm";
28042
- return null;
28043
- }
28044
-
28045
- // src/utils/detect.ts
28046
- async function detectPackageManager2(cwd) {
28047
- const fromUa = detectPmFromUserAgent(process.env.npm_config_user_agent);
28048
- if (fromUa) return fromUa;
28049
- if (await import_fs_extra10.default.pathExists(path.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
28050
- if (await import_fs_extra10.default.pathExists(path.join(cwd, "yarn.lock"))) return "yarn";
28051
- if (await import_fs_extra10.default.pathExists(path.join(cwd, "bun.lockb"))) return "bun";
28052
- return "npm";
28053
- }
28054
- async function detectProject(cwd) {
28055
- const packageJsonPath = path.join(cwd, "package.json");
28056
- if (!await import_fs_extra10.default.pathExists(packageJsonPath)) {
28057
- throw new Error(
28058
- "No package.json found. Please run this command in a Next.js project."
28059
- );
28060
- }
28061
- const packageJson = await import_fs_extra10.default.readJson(packageJsonPath);
28062
- const deps = {
28063
- ...packageJson.dependencies,
28064
- ...packageJson.devDependencies
28065
- };
28066
- const isNextJs = "next" in deps;
28067
- if (!isNextJs) {
28068
- throw new Error(
28069
- "Next.js not found in dependencies. Please run this in a Next.js project."
28070
- );
28071
- }
28072
- const nextVersion = deps.next?.replace(/[\^~]/, "") || null;
28073
- const srcDir = await import_fs_extra10.default.pathExists(path.join(cwd, "src"));
28074
- const appDirPaths = srcDir ? [path.join(cwd, "src", "app")] : [path.join(cwd, "app")];
28075
- let isAppRouter = false;
28076
- for (const appPath of appDirPaths) {
28077
- if (await import_fs_extra10.default.pathExists(appPath)) {
28078
- isAppRouter = true;
28079
- break;
28080
- }
28081
- }
28082
- if (!isAppRouter) {
28083
- throw new Error(
28084
- "App Router not detected. Nextly requires Next.js App Router (app/ directory)."
28085
- );
28086
- }
28087
- const hasTypescript = await import_fs_extra10.default.pathExists(path.join(cwd, "tsconfig.json"));
28088
- const packageManager = await detectPackageManager2(cwd);
28089
- const appDir = srcDir ? "src/app" : "app";
28090
- return {
28091
- isNextJs,
28092
- isAppRouter,
28093
- hasTypescript,
28094
- packageManager,
28095
- nextVersion,
28096
- srcDir,
28097
- appDir
28098
- };
28099
- }
28100
-
28101
- // src/utils/template.ts
28102
- var import_fs_extra11 = __toESM(require_lib(), 1);
28103
- var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
28104
- ".ts",
28105
- ".tsx",
28106
- ".js",
28107
- ".jsx",
28108
- ".json",
28109
- ".env",
28110
- ".md",
28111
- ".css",
28112
- ".html",
28113
- ".mjs",
28114
- ".cjs"
28115
- ]);
28116
- var SKIP_FILES = /* @__PURE__ */ new Set([".DS_Store", "Thumbs.db", ".gitkeep"]);
28117
- function resolveTemplatePath(localTemplatePath) {
28118
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
28119
- const fromDist = path.resolve(__dirname, "../templates");
28120
- if (import_fs_extra11.default.existsSync(fromDist)) {
28121
- return fromDist;
28122
- }
28123
- const fromSrc = path.resolve(__dirname, "../../templates");
28124
- if (import_fs_extra11.default.existsSync(fromSrc)) {
28125
- return fromSrc;
28126
- }
28127
- throw new Error(
28128
- "Could not find templates directory. Use --local-template to specify the templates path, or ensure templates are bundled."
28129
- );
28130
- }
28131
- function buildPlaceholderMap(options) {
28132
- const { database, databaseUrl } = options;
28133
- return {
28134
- "{{databaseDialect}}": database.type,
28135
- "{{databaseUrl}}": databaseUrl || database.envExample
28136
- };
28137
- }
28138
- async function replacePlaceholdersInFile(filePath, placeholders) {
28139
- const ext = path.extname(filePath).toLowerCase();
28140
- const basename = path.basename(filePath);
28141
- const isTextFile = TEXT_EXTENSIONS.has(ext) || basename.startsWith(".env") || basename === ".gitignore";
28142
- if (!isTextFile) return;
28143
- let content = await import_fs_extra11.default.readFile(filePath, "utf-8");
28144
- let changed = false;
28145
- for (const [placeholder, value] of Object.entries(placeholders)) {
28146
- if (content.includes(placeholder)) {
28147
- content = content.replaceAll(placeholder, value);
28148
- changed = true;
28149
- }
28150
- }
28151
- if (changed) {
28152
- await import_fs_extra11.default.writeFile(filePath, content, "utf-8");
28153
- }
28154
- }
28155
- async function replacePlaceholders(dir, placeholders) {
28156
- const entries = await import_fs_extra11.default.readdir(dir, { withFileTypes: true });
28157
- for (const entry of entries) {
28158
- const fullPath = path.join(dir, entry.name);
28159
- if (entry.isDirectory()) {
28160
- if (entry.name === "node_modules" || entry.name === ".git") continue;
28161
- await replacePlaceholders(fullPath, placeholders);
28162
- } else if (entry.isFile()) {
28163
- await replacePlaceholdersInFile(fullPath, placeholders);
28164
- }
28165
- }
28166
- }
28167
- var PINNED_VERSIONS = {
28168
- // Next.js ecosystem — resolved at runtime via fetchLatestVersion()
28169
- // (see generatePackageJson)
28170
- react: "^19.1.0",
28171
- "react-dom": "^19.1.0",
28172
- // Dev dependencies
28173
- typescript: "^5",
28174
- "@types/node": "^20",
28175
- "@types/react": "^19",
28176
- "@types/react-dom": "^19",
28177
- "@tailwindcss/postcss": "^4",
28178
- tailwindcss: "^4",
28179
- eslint: "^9"
28180
- };
28181
- var RUNTIME_RESOLVED_PACKAGES = ["next", "eslint-config-next"];
28182
- var NEXTLY_PACKAGES = [
28183
- "nextly",
28184
- "@nextlyhq/admin",
28185
- "@nextlyhq/adapter-drizzle",
28186
- "@nextlyhq/adapter-postgres",
28187
- "@nextlyhq/adapter-mysql",
28188
- "@nextlyhq/adapter-sqlite"
28189
- ];
28190
- var resolvedNextlyVersions = null;
28191
- async function fetchLatestVersion(pkg) {
28296
+ }
28297
+ };
28298
+
28299
+ // src/prompts/project-name.ts
28300
+ var import_fs_extra10 = __toESM(require_lib(), 1);
28301
+ async function isExistingNextProject(cwd) {
28302
+ const packageJsonPath = path.join(cwd, "package.json");
28303
+ if (!await import_fs_extra10.default.pathExists(packageJsonPath)) return false;
28192
28304
  try {
28193
- const res = await fetch(
28194
- `https://registry.npmjs.org/-/package/${encodeURIComponent(pkg)}/dist-tags`,
28195
- { signal: AbortSignal.timeout(5e3) }
28196
- );
28197
- if (!res.ok) return "latest";
28198
- const data = await res.json();
28199
- return data.latest ? `^${data.latest}` : "latest";
28305
+ const packageJson = await import_fs_extra10.default.readJson(packageJsonPath);
28306
+ const deps = {
28307
+ ...packageJson.dependencies,
28308
+ ...packageJson.devDependencies
28309
+ };
28310
+ return "next" in deps;
28200
28311
  } catch {
28201
- return "latest";
28312
+ return false;
28202
28313
  }
28203
28314
  }
28204
- async function resolveNextlyVersions() {
28205
- if (resolvedNextlyVersions) return resolvedNextlyVersions;
28206
- const entries = await Promise.all(
28207
- NEXTLY_PACKAGES.map(
28208
- async (pkg) => [pkg, await fetchLatestVersion(pkg)]
28209
- )
28210
- );
28211
- resolvedNextlyVersions = Object.fromEntries(entries);
28212
- return resolvedNextlyVersions;
28315
+
28316
+ // src/prompts/template.ts
28317
+ var import_picocolors2 = __toESM(require_picocolors2(), 1);
28318
+ function getTemplatePromptOptions() {
28319
+ return [
28320
+ {
28321
+ value: "blank",
28322
+ label: "Blank project",
28323
+ hint: "Start fresh with an empty config"
28324
+ },
28325
+ {
28326
+ value: "blog",
28327
+ label: "Blog",
28328
+ hint: "Posts, authors, categories, clean design"
28329
+ },
28330
+ {
28331
+ value: "_coming_soon",
28332
+ label: import_picocolors2.default.dim("More templates coming soon"),
28333
+ hint: "website, portfolio, e-commerce"
28334
+ }
28335
+ ];
28213
28336
  }
28214
- var resolvedRuntimeVersions = null;
28215
- async function resolveRuntimeVersions() {
28216
- if (resolvedRuntimeVersions) return resolvedRuntimeVersions;
28217
- const FALLBACKS = {
28218
- next: "^16.1.0",
28219
- "eslint-config-next": "^16.1.0"
28220
- };
28221
- const entries = await Promise.all(
28222
- RUNTIME_RESOLVED_PACKAGES.map(async (pkg) => {
28223
- const version2 = await fetchLatestVersion(pkg);
28224
- return [pkg, version2 === "latest" ? FALLBACKS[pkg] : version2];
28225
- })
28226
- );
28227
- resolvedRuntimeVersions = Object.fromEntries(entries);
28228
- return resolvedRuntimeVersions;
28337
+ function isValidTemplateSelection(value) {
28338
+ return value !== "_coming_soon";
28229
28339
  }
28230
- async function generatePackageJson(projectName, database, useYalc = false) {
28231
- const runtimeVersions = await resolveRuntimeVersions();
28232
- const dependencies = {
28233
- next: runtimeVersions.next,
28234
- react: PINNED_VERSIONS.react,
28235
- "react-dom": PINNED_VERSIONS["react-dom"]
28236
- };
28237
- dependencies["@tanstack/react-query"] = "^5.62.0";
28238
- if (!useYalc) {
28239
- const versions = await resolveNextlyVersions();
28240
- dependencies["nextly"] = versions["nextly"];
28241
- dependencies["@nextlyhq/admin"] = versions["@nextlyhq/admin"];
28242
- dependencies["@nextlyhq/ui"] = versions["@nextlyhq/ui"] || "latest";
28243
- dependencies["@nextlyhq/adapter-drizzle"] = versions["@nextlyhq/adapter-drizzle"];
28244
- dependencies[database.adapter] = versions[database.adapter] || "latest";
28245
- dependencies["@nextlyhq/plugin-form-builder"] = versions["@nextlyhq/plugin-form-builder"] || "latest";
28246
- }
28247
- const devDependencies = {
28248
- typescript: PINNED_VERSIONS.typescript,
28249
- "@types/node": PINNED_VERSIONS["@types/node"],
28250
- "@types/react": PINNED_VERSIONS["@types/react"],
28251
- "@types/react-dom": PINNED_VERSIONS["@types/react-dom"],
28252
- "@tailwindcss/postcss": PINNED_VERSIONS["@tailwindcss/postcss"],
28253
- tailwindcss: PINNED_VERSIONS.tailwindcss,
28254
- eslint: PINNED_VERSIONS.eslint,
28255
- "eslint-config-next": runtimeVersions["eslint-config-next"],
28256
- // Pagefind powers /search in the blog template. Zero-config
28257
- // static index generated at `next build` time. Templates that
28258
- // don't ship a /search page simply won't invoke it.
28259
- pagefind: "^1.1.0"
28260
- };
28261
- const pkg = {
28262
- name: projectName,
28263
- version: "0.1.0",
28264
- private: true,
28265
- scripts: {
28266
- // F1 PR 4: dev now boots Nextly in single-process mode via `next dev`.
28267
- // The lazy drizzle-kit/api import (PR 1) plus the in-process HMR
28268
- // listener (PR 2) replaced the wrapper that previously owned the
28269
- // terminal, schema prompts, and child supervision. `nextly dev` is
28270
- // gone; the only supported dev command is the standard `next dev`.
28271
- dev: "next dev --turbopack",
28272
- // Build: migrate DB + compile Next.js + (if present) generate
28273
- // the Pagefind search index. Templates without the search
28274
- // script silently skip the last step.
28275
- build: "nextly migrate && next build && (test -f scripts/build-search-index.mjs && node scripts/build-search-index.mjs || true)",
28276
- "search:index": "node scripts/build-search-index.mjs",
28277
- start: "next start",
28278
- lint: "next lint",
28279
- nextly: "nextly",
28280
- // First-time setup: sync schema + seed system permissions. Demo
28281
- // content is seeded separately from the admin UI (visit /welcome
28282
- // after running `pnpm dev` and completing /admin/setup).
28283
- "db:setup": "nextly db:sync",
28284
- "db:migrate": "nextly migrate",
28285
- "db:migrate:status": "nextly migrate:status",
28286
- "db:migrate:fresh": "nextly migrate:fresh",
28287
- "db:migrate:reset": "nextly migrate:reset",
28288
- "types:generate": "nextly generate:types"
28289
- },
28290
- dependencies,
28291
- devDependencies
28292
- };
28293
- return JSON.stringify(pkg, null, 2) + "\n";
28340
+
28341
+ // src/utils/detect.ts
28342
+ var import_fs_extra11 = __toESM(require_lib(), 1);
28343
+
28344
+ // src/utils/detect-pm-from-user-agent.ts
28345
+ function detectPmFromUserAgent(userAgent) {
28346
+ if (!userAgent) return null;
28347
+ if (userAgent.startsWith("pnpm/")) return "pnpm";
28348
+ if (userAgent.startsWith("yarn/")) return "yarn";
28349
+ if (userAgent.startsWith("bun/")) return "bun";
28350
+ if (userAgent.startsWith("npm/")) return "npm";
28351
+ return null;
28294
28352
  }
28295
- async function copyTemplate(options) {
28296
- const {
28297
- projectName,
28298
- projectType,
28299
- targetDir,
28300
- database,
28301
- databaseUrl,
28302
- useYalc = false,
28303
- approach,
28304
- templateSource
28305
- } = options;
28306
- if (targetDir !== process.cwd() && await import_fs_extra11.default.pathExists(targetDir)) {
28307
- throw new Error(
28308
- `Directory "${path.basename(targetDir)}" already exists. Please choose a different name.`
28309
- );
28310
- }
28311
- let baseDir;
28312
- let typeDir;
28313
- if (templateSource) {
28314
- baseDir = templateSource.basePath;
28315
- typeDir = templateSource.templatePath;
28316
- } else {
28317
- const templatesRoot = resolveTemplatePath();
28318
- baseDir = path.join(templatesRoot, "base");
28319
- typeDir = path.join(templatesRoot, projectType);
28320
- }
28321
- if (!await import_fs_extra11.default.pathExists(baseDir)) {
28353
+
28354
+ // src/utils/detect.ts
28355
+ async function detectPackageManager2(cwd) {
28356
+ const fromUa = detectPmFromUserAgent(process.env.npm_config_user_agent);
28357
+ if (fromUa) return fromUa;
28358
+ if (await import_fs_extra11.default.pathExists(path.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
28359
+ if (await import_fs_extra11.default.pathExists(path.join(cwd, "yarn.lock"))) return "yarn";
28360
+ if (await import_fs_extra11.default.pathExists(path.join(cwd, "bun.lockb"))) return "bun";
28361
+ return "npm";
28362
+ }
28363
+ async function detectProject(cwd) {
28364
+ const packageJsonPath = path.join(cwd, "package.json");
28365
+ if (!await import_fs_extra11.default.pathExists(packageJsonPath)) {
28322
28366
  throw new Error(
28323
- `Base template not found at ${baseDir}. The package may be corrupted or the download failed.`
28367
+ "No package.json found. Please run this command in a Next.js project."
28324
28368
  );
28325
28369
  }
28326
- if (!await import_fs_extra11.default.pathExists(typeDir)) {
28370
+ const packageJson = await import_fs_extra11.default.readJson(packageJsonPath);
28371
+ const deps = {
28372
+ ...packageJson.dependencies,
28373
+ ...packageJson.devDependencies
28374
+ };
28375
+ const isNextJs = "next" in deps;
28376
+ if (!isNextJs) {
28327
28377
  throw new Error(
28328
- `Template "${projectType}" not found at ${typeDir}. Available templates: blank, blog.`
28378
+ "Next.js not found in dependencies. Please run this in a Next.js project."
28329
28379
  );
28330
28380
  }
28331
- await import_fs_extra11.default.copy(baseDir, targetDir, {
28332
- filter: (_src) => {
28333
- const basename = path.basename(_src);
28334
- return !SKIP_FILES.has(basename);
28381
+ const nextVersion = deps.next?.replace(/[\^~]/, "") || null;
28382
+ const srcDir = await import_fs_extra11.default.pathExists(path.join(cwd, "src"));
28383
+ const appDirPaths = srcDir ? [path.join(cwd, "src", "app")] : [path.join(cwd, "app")];
28384
+ let isAppRouter = false;
28385
+ for (const appPath of appDirPaths) {
28386
+ if (await import_fs_extra11.default.pathExists(appPath)) {
28387
+ isAppRouter = true;
28388
+ break;
28335
28389
  }
28336
- });
28337
- const templateSrcDir = path.join(typeDir, "src");
28338
- if (await import_fs_extra11.default.pathExists(templateSrcDir)) {
28339
- await import_fs_extra11.default.copy(templateSrcDir, path.join(targetDir, "src"), {
28340
- overwrite: true,
28341
- filter: (_src) => {
28342
- const basename = path.basename(_src);
28343
- return !SKIP_FILES.has(basename);
28344
- }
28345
- });
28346
28390
  }
28347
- const templateRootConfig = path.join(typeDir, "nextly.config.ts");
28348
- if (await import_fs_extra11.default.pathExists(templateRootConfig)) {
28349
- await import_fs_extra11.default.copy(
28350
- templateRootConfig,
28351
- path.join(targetDir, "nextly.config.ts"),
28352
- { overwrite: true }
28391
+ if (!isAppRouter) {
28392
+ throw new Error(
28393
+ "App Router not detected. Nextly requires Next.js App Router (app/ directory)."
28353
28394
  );
28354
28395
  }
28355
- const configsDir = path.join(typeDir, "configs");
28356
- if (approach && await import_fs_extra11.default.pathExists(configsDir)) {
28357
- const configFileName = approach === "code-first" ? "codefirst.config.ts" : `${approach}.config.ts`;
28358
- const configSrc = path.join(configsDir, configFileName);
28359
- if (await import_fs_extra11.default.pathExists(configSrc)) {
28360
- await import_fs_extra11.default.copy(configSrc, path.join(targetDir, "nextly.config.ts"), {
28361
- overwrite: true
28362
- });
28363
- }
28364
- const sharedSrc = path.join(configsDir, "shared.ts");
28365
- if (await import_fs_extra11.default.pathExists(sharedSrc)) {
28366
- await import_fs_extra11.default.copy(sharedSrc, path.join(targetDir, "shared.ts"), {
28367
- overwrite: true
28368
- });
28369
- }
28370
- }
28371
- const frontendPagePath = path.join(
28372
- targetDir,
28373
- "src",
28374
- "app",
28375
- "(frontend)",
28376
- "page.tsx"
28377
- );
28378
- const basePagePath = path.join(targetDir, "src", "app", "page.tsx");
28379
- if (await import_fs_extra11.default.pathExists(frontendPagePath) && await import_fs_extra11.default.pathExists(basePagePath)) {
28380
- await import_fs_extra11.default.remove(basePagePath);
28381
- }
28382
- const packageJsonContent = await generatePackageJson(
28383
- projectName,
28384
- database,
28385
- useYalc
28386
- );
28387
- await import_fs_extra11.default.writeFile(
28388
- path.join(targetDir, "package.json"),
28389
- packageJsonContent,
28390
- "utf-8"
28391
- );
28392
- if (database.type === "sqlite") {
28393
- await import_fs_extra11.default.ensureDir(path.join(targetDir, "data"));
28394
- }
28395
- const placeholders = buildPlaceholderMap({ database, databaseUrl });
28396
- if (approach) {
28397
- placeholders["{{approach}}"] = approach;
28398
- }
28399
- await replacePlaceholders(targetDir, placeholders);
28396
+ const hasTypescript = await import_fs_extra11.default.pathExists(path.join(cwd, "tsconfig.json"));
28397
+ const packageManager = await detectPackageManager2(cwd);
28398
+ const appDir = srcDir ? "src/app" : "app";
28399
+ return {
28400
+ isNextJs,
28401
+ isAppRouter,
28402
+ hasTypescript,
28403
+ packageManager,
28404
+ nextVersion,
28405
+ srcDir,
28406
+ appDir
28407
+ };
28400
28408
  }
28401
28409
 
28402
28410
  // src/create-nextly.ts
@@ -28652,7 +28660,8 @@ async function createNextly(options = {}) {
28652
28660
  projectInfo,
28653
28661
  database,
28654
28662
  useYalc,
28655
- isFreshProject
28663
+ isFreshProject,
28664
+ projectType
28656
28665
  );
28657
28666
  s3.stop("Dependencies installed");
28658
28667
  capture("install_completed", {
@@ -28748,5 +28757,5 @@ async function createNextly(options = {}) {
28748
28757
  */
28749
28758
 
28750
28759
  export { __commonJS, __require, __toESM, capture, createNextly, init, shutdown };
28751
- //# sourceMappingURL=chunk-AYJ2RKVJ.mjs.map
28752
- //# sourceMappingURL=chunk-AYJ2RKVJ.mjs.map
28760
+ //# sourceMappingURL=chunk-6NVRVAZA.mjs.map
28761
+ //# sourceMappingURL=chunk-6NVRVAZA.mjs.map