create-nextly-app 0.0.2-alpha.57 → 0.0.2-alpha.58

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/dist/index.cjs CHANGED
@@ -21125,6 +21125,47 @@ var import_fs_extra8 = __toESM(require_lib());
21125
21125
  var PROJECT_TYPES_WITH_FORM_BUILDER = /* @__PURE__ */ new Set([
21126
21126
  "blog"
21127
21127
  ]);
21128
+ var FONT_PACKAGE_RANGE = "^5.3.0";
21129
+ var FONT_PACKAGE_PATTERN = /@fontsource(?:-variable)?\/[a-z0-9-]+/g;
21130
+ async function collectFontDependencies(templateDirs) {
21131
+ const effective = await effectiveTemplateFiles(templateDirs);
21132
+ const found = /* @__PURE__ */ new Set();
21133
+ for (const file of effective.values()) {
21134
+ const contents = await import_fs_extra8.default.readFile(file, "utf8");
21135
+ for (const match of contents.matchAll(FONT_PACKAGE_PATTERN)) {
21136
+ found.add(match[0]);
21137
+ }
21138
+ }
21139
+ return [...found].sort();
21140
+ }
21141
+ async function effectiveTemplateFiles(templateDirs) {
21142
+ const effective = /* @__PURE__ */ new Map();
21143
+ for (const root of templateDirs) {
21144
+ if (!root || !await import_fs_extra8.default.pathExists(root)) continue;
21145
+ for (const file of await walkSourceFiles(root)) {
21146
+ effective.set(path14__default.default.relative(root, file), file);
21147
+ }
21148
+ }
21149
+ return effective;
21150
+ }
21151
+ var SEARCH_INDEX_SCRIPT = path14__default.default.join("scripts", "build-search-index.mjs");
21152
+ async function projectHasSearchIndexScript(targetDir) {
21153
+ return import_fs_extra8.default.pathExists(path14__default.default.join(targetDir, SEARCH_INDEX_SCRIPT));
21154
+ }
21155
+ async function walkSourceFiles(root) {
21156
+ const out = [];
21157
+ const entries = await import_fs_extra8.default.readdir(root, { withFileTypes: true });
21158
+ for (const entry of entries) {
21159
+ const full = path14__default.default.join(root, entry.name);
21160
+ if (entry.isDirectory()) {
21161
+ if (entry.name === "node_modules" || entry.name === ".next") continue;
21162
+ out.push(...await walkSourceFiles(full));
21163
+ } else if (/\.(?:tsx?|jsx?|mjs|cjs|css)$/.test(entry.name)) {
21164
+ out.push(full);
21165
+ }
21166
+ }
21167
+ return out;
21168
+ }
21128
21169
  function projectUsesFormBuilder(projectType) {
21129
21170
  return PROJECT_TYPES_WITH_FORM_BUILDER.has(projectType);
21130
21171
  }
@@ -21146,6 +21187,15 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
21146
21187
  ".cjs"
21147
21188
  ]);
21148
21189
  var SKIP_FILES = /* @__PURE__ */ new Set([".DS_Store", "Thumbs.db", ".gitkeep"]);
21190
+ var IGNORE_FILE_IN_TEMPLATE = "gitignore";
21191
+ var IGNORE_FILE_IN_PROJECT = ".gitignore";
21192
+ async function restoreIgnoreFile(targetDir) {
21193
+ const shipped = path14__default.default.join(targetDir, IGNORE_FILE_IN_TEMPLATE);
21194
+ if (!await import_fs_extra8.default.pathExists(shipped)) return;
21195
+ await import_fs_extra8.default.move(shipped, path14__default.default.join(targetDir, IGNORE_FILE_IN_PROJECT), {
21196
+ overwrite: true
21197
+ });
21198
+ }
21149
21199
  function resolveTemplatePath(localTemplatePath) {
21150
21200
  const __dirname = path14__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
21151
21201
  const fromDist = path14__default.default.resolve(__dirname, "../templates");
@@ -21269,7 +21319,7 @@ async function resolveRuntimeVersions() {
21269
21319
  resolvedRuntimeVersions = Object.fromEntries(entries);
21270
21320
  return resolvedRuntimeVersions;
21271
21321
  }
21272
- async function generatePackageJson(projectName, database, useYalc = false, projectType = "blank") {
21322
+ async function generatePackageJson(projectName, database, useYalc = false, projectType = "blank", templateDirs = [], targetDir) {
21273
21323
  if (projectType === "plugin") {
21274
21324
  return generatePluginPackageJson(projectName, useYalc);
21275
21325
  }
@@ -21281,6 +21331,9 @@ async function generatePackageJson(projectName, database, useYalc = false, proje
21281
21331
  };
21282
21332
  dependencies["@tanstack/react-query"] = "^5.62.0";
21283
21333
  dependencies["lucide-react"] = "^0.544.0";
21334
+ for (const font of await collectFontDependencies(templateDirs)) {
21335
+ dependencies[font] = FONT_PACKAGE_RANGE;
21336
+ }
21284
21337
  if (!useYalc) {
21285
21338
  const versions = await resolveNextlyVersions(
21286
21339
  templateNextlyChannel(projectType)
@@ -21303,12 +21356,10 @@ async function generatePackageJson(projectName, database, useYalc = false, proje
21303
21356
  "@tailwindcss/postcss": PINNED_VERSIONS["@tailwindcss/postcss"],
21304
21357
  tailwindcss: PINNED_VERSIONS.tailwindcss,
21305
21358
  eslint: PINNED_VERSIONS.eslint,
21306
- "eslint-config-next": runtimeVersions["eslint-config-next"],
21307
- // Pagefind powers /search in the blog template. Zero-config
21308
- // static index generated at `next build` time. Templates that
21309
- // don't ship a /search page simply won't invoke it.
21310
- pagefind: "^1.1.0"
21359
+ "eslint-config-next": runtimeVersions["eslint-config-next"]
21311
21360
  };
21361
+ const shipsSearchIndex = targetDir ? await projectHasSearchIndexScript(targetDir) : false;
21362
+ if (shipsSearchIndex) devDependencies.pagefind = "^1.1.0";
21312
21363
  const pkg = {
21313
21364
  name: projectName,
21314
21365
  version: "0.1.0",
@@ -21320,11 +21371,25 @@ async function generatePackageJson(projectName, database, useYalc = false, proje
21320
21371
  // prompts, and child supervision. `nextly dev` is gone; the only
21321
21372
  // supported dev command is the standard `next dev`.
21322
21373
  dev: "next dev --turbopack",
21323
- // Build: migrate DB + compile Next.js + (if present) generate
21324
- // the Pagefind search index. Templates without the search
21325
- // script silently skip the last step.
21326
- build: "nextly migrate && next build && (test -f scripts/build-search-index.mjs && node scripts/build-search-index.mjs || true)",
21327
- "search:index": "node scripts/build-search-index.mjs",
21374
+ // Build: migrate the database, compile Next.js, and generate the Pagefind
21375
+ // search index for the templates that ship its builder.
21376
+ //
21377
+ // Whether that last step appears is decided HERE, from the files the
21378
+ // scaffold actually received, rather than by a shell conditional in the
21379
+ // script. Two reasons, and both were live:
21380
+ //
21381
+ // `npm run` uses cmd.exe on Windows, which has no `test` and no `true` —
21382
+ // so the `(test -f … || true)` form this replaces failed for every Windows
21383
+ // user, after `next build` had already succeeded. `&&` is all that remains,
21384
+ // and cmd.exe understands it.
21385
+ //
21386
+ // And `|| true` turned a failed index build into a successful one. The
21387
+ // search page then ships pointing at an index that was never written,
21388
+ // which fails in the browser rather than in CI.
21389
+ build: shipsSearchIndex ? "nextly migrate && next build && node scripts/build-search-index.mjs" : "nextly migrate && next build",
21390
+ // Offered only where the file exists. A script that always fails is worse
21391
+ // than an absent one: it reads as a supported command.
21392
+ ...shipsSearchIndex ? { "search:index": "node scripts/build-search-index.mjs" } : {},
21328
21393
  start: "next start",
21329
21394
  lint: "next lint",
21330
21395
  nextly: "nextly",
@@ -21334,8 +21399,11 @@ async function generatePackageJson(projectName, database, useYalc = false, proje
21334
21399
  "db:setup": "nextly db:sync",
21335
21400
  "db:migrate": "nextly migrate",
21336
21401
  "db:migrate:status": "nextly migrate:status",
21402
+ // No `db:migrate:reset`: `nextly migrate:reset` is not a command the CLI
21403
+ // registers, so the script it generated failed for every scaffolded
21404
+ // project. `migrate:fresh` is the one that drops all tables and re-runs
21405
+ // the migrations, and it is already exposed above.
21337
21406
  "db:migrate:fresh": "nextly migrate:fresh",
21338
- "db:migrate:reset": "nextly migrate:reset",
21339
21407
  "types:generate": "nextly generate:types"
21340
21408
  },
21341
21409
  dependencies,
@@ -21437,13 +21505,46 @@ function generatePnpmWorkspaceYaml() {
21437
21505
  # compiled binding (sqlite apps crash at boot) and sharp/esbuild degrade.
21438
21506
  #
21439
21507
  # pnpm 11+ reads \`allowBuilds\`; pnpm 10.6+ reads \`onlyBuiltDependencies\`.
21440
- # npm, yarn, and pnpm 9 ignore this file (they run build scripts by default).
21508
+ # npm and yarn ignore this file; pnpm 9 needs neither key, because it runs
21509
+ # build scripts by default.
21510
+ #
21511
+ # It does still READ the file, which is what the empty \`packages\` list is
21512
+ # for: through pnpm 9.3 the presence of this file declares a workspace, and
21513
+ # a missing \`packages\` key fails the install outright. This app has no
21514
+ # workspace members.
21515
+ packages: []
21441
21516
  allowBuilds:
21442
21517
  ${allowBuilds}
21443
21518
  onlyBuiltDependencies:
21444
21519
  ${onlyBuilt}
21445
21520
  `;
21446
21521
  }
21522
+ function generateNpmrc(packageManager) {
21523
+ if (packageManager !== "pnpm") return null;
21524
+ return `# This project is a single package, not a monorepo. pnpm 9 treats the
21525
+ # pnpm-workspace.yaml shipped for its build allowlist as declaring a
21526
+ # workspace root, which makes \`pnpm add <pkg>\` demand a -w flag. There is no
21527
+ # other package here for that check to protect.
21528
+ ${NPMRC_WORKSPACE_ROOT_KEY}=true
21529
+ `;
21530
+ }
21531
+ var NPMRC_WORKSPACE_ROOT_KEY = "ignore-workspace-root-check";
21532
+ async function writeScaffoldNpmrc(targetDir, packageManager) {
21533
+ const addition = generateNpmrc(packageManager);
21534
+ if (!addition) return;
21535
+ const npmrcPath = path14__default.default.join(targetDir, ".npmrc");
21536
+ const link = await import_fs_extra8.default.lstat(npmrcPath).catch(() => null);
21537
+ if (link?.isSymbolicLink()) return;
21538
+ if (link && link.nlink > 1) return;
21539
+ const existing = link ? await import_fs_extra8.default.readFile(npmrcPath, "utf-8") : "";
21540
+ const alreadyDeclared = new RegExp(
21541
+ `^\\s*${NPMRC_WORKSPACE_ROOT_KEY}\\s*=`,
21542
+ "m"
21543
+ ).test(existing);
21544
+ if (alreadyDeclared) return;
21545
+ const separator = existing === "" || existing.endsWith("\n") ? "" : "\n";
21546
+ await import_fs_extra8.default.writeFile(npmrcPath, existing + separator + addition, "utf-8");
21547
+ }
21447
21548
  async function copyTemplate(options) {
21448
21549
  const {
21449
21550
  projectName,
@@ -21454,6 +21555,7 @@ async function copyTemplate(options) {
21454
21555
  useYalc = false,
21455
21556
  approach,
21456
21557
  templateSource,
21558
+ packageManager,
21457
21559
  allowExistingTarget = false
21458
21560
  } = options;
21459
21561
  if (!allowExistingTarget && targetDir !== process.cwd() && await import_fs_extra8.default.pathExists(targetDir)) {
@@ -21472,7 +21574,13 @@ async function copyTemplate(options) {
21472
21574
  typeDir = path14__default.default.join(templatesRoot, projectType);
21473
21575
  }
21474
21576
  if (projectType === "plugin") {
21475
- await copyPluginTemplate({ projectName, typeDir, targetDir, useYalc });
21577
+ await copyPluginTemplate({
21578
+ projectName,
21579
+ typeDir,
21580
+ targetDir,
21581
+ useYalc,
21582
+ packageManager
21583
+ });
21476
21584
  return;
21477
21585
  }
21478
21586
  if (!await import_fs_extra8.default.pathExists(baseDir)) {
@@ -21531,6 +21639,13 @@ async function copyTemplate(options) {
21531
21639
  overwrite: false
21532
21640
  });
21533
21641
  }
21642
+ const templateScriptsDir = path14__default.default.join(typeDir, "scripts");
21643
+ if (await import_fs_extra8.default.pathExists(templateScriptsDir)) {
21644
+ await import_fs_extra8.default.copy(templateScriptsDir, path14__default.default.join(targetDir, "scripts"), {
21645
+ overwrite: false,
21646
+ filter: (src) => !SKIP_FILES.has(path14__default.default.basename(src))
21647
+ });
21648
+ }
21534
21649
  const frontendPagePath = path14__default.default.join(
21535
21650
  targetDir,
21536
21651
  "src",
@@ -21546,7 +21661,9 @@ async function copyTemplate(options) {
21546
21661
  projectName,
21547
21662
  database,
21548
21663
  useYalc,
21549
- projectType
21664
+ projectType,
21665
+ [baseDir, typeDir],
21666
+ targetDir
21550
21667
  );
21551
21668
  await import_fs_extra8.default.writeFile(
21552
21669
  path14__default.default.join(targetDir, "package.json"),
@@ -21558,6 +21675,8 @@ async function copyTemplate(options) {
21558
21675
  generatePnpmWorkspaceYaml(),
21559
21676
  "utf-8"
21560
21677
  );
21678
+ await restoreIgnoreFile(targetDir);
21679
+ await writeScaffoldNpmrc(targetDir, packageManager);
21561
21680
  if (database.type === "sqlite") {
21562
21681
  await import_fs_extra8.default.ensureDir(path14__default.default.join(targetDir, "data"));
21563
21682
  }
@@ -21573,7 +21692,7 @@ async function copyTemplate(options) {
21573
21692
  await replacePlaceholders(targetDir, placeholders);
21574
21693
  }
21575
21694
  async function copyPluginTemplate(opts) {
21576
- const { projectName, typeDir, targetDir, useYalc } = opts;
21695
+ const { projectName, typeDir, targetDir, useYalc, packageManager } = opts;
21577
21696
  if (!await import_fs_extra8.default.pathExists(typeDir)) {
21578
21697
  throw new Error(
21579
21698
  `Plugin template not found at ${typeDir}. The package may be corrupted or the download failed.`
@@ -21602,6 +21721,8 @@ async function copyPluginTemplate(opts) {
21602
21721
  generatePnpmWorkspaceYaml(),
21603
21722
  "utf-8"
21604
21723
  );
21724
+ await restoreIgnoreFile(targetDir);
21725
+ await writeScaffoldNpmrc(targetDir, packageManager);
21605
21726
  const nextlyRange = await resolvePluginNextlyRange(useYalc);
21606
21727
  await replacePlaceholders(
21607
21728
  targetDir,
@@ -25201,6 +25322,7 @@ async function createNextly(options = {}) {
25201
25322
  } else {
25202
25323
  s3.start("Scaffolding project...");
25203
25324
  }
25325
+ const scaffoldPackageManager = await detectPackageManager(targetDir);
25204
25326
  await copyTemplate({
25205
25327
  projectName,
25206
25328
  projectType,
@@ -25210,6 +25332,7 @@ async function createNextly(options = {}) {
25210
25332
  useYalc,
25211
25333
  approach,
25212
25334
  templateSource,
25335
+ packageManager: scaffoldPackageManager,
25213
25336
  // Suppress copyTemplate's "directory already exists" guard when the
25214
25337
  // installer has already negotiated the conflict with the user
25215
25338
  // (either by emptying the dir or accepting an overlay). Without