@adbayb/stack 2.39.0 → 2.40.0

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.
Files changed (30) hide show
  1. package/dist/index.js +150 -78
  2. package/package.json +1 -2
  3. package/templates/{single-project/package.json.tmpl → multi-projects/package.json} +6 -11
  4. package/templates/multi-projects/{pnpm-workspace.yaml.tmpl → pnpm-workspace.yaml} +5 -0
  5. package/templates/{multi-projects/package.json.tmpl → single-project/package.json} +6 -11
  6. package/templates/single-project/{pnpm-workspace.yaml.tmpl → pnpm-workspace.yaml} +5 -0
  7. package/templates/multi-projects/.npmrc.tmpl +0 -1
  8. package/templates/single-project/.npmrc.tmpl +0 -1
  9. /package/templates/multi-projects/.changeset/{config.json.tmpl → config.json} +0 -0
  10. /package/templates/multi-projects/.changeset/{welcome.md.tmpl → welcome.md} +0 -0
  11. /package/templates/multi-projects/.github/{PULL_REQUEST_TEMPLATE.md.tmpl → PULL_REQUEST_TEMPLATE.md} +0 -0
  12. /package/templates/multi-projects/{.nvmrc.tmpl → .nvmrc} +0 -0
  13. /package/templates/multi-projects/{CONTRIBUTING.md.tmpl → CONTRIBUTING.md} +0 -0
  14. /package/templates/multi-projects/{LICENSE.tmpl → LICENSE} +0 -0
  15. /package/templates/multi-projects/examples/default/{package.json.tmpl → package.json} +0 -0
  16. /package/templates/multi-projects/examples/default/src/{index.ts.tmpl → index.ts} +0 -0
  17. /package/templates/multi-projects/libraries/{{projectName}}/{README.md.tmpl → README.md} +0 -0
  18. /package/templates/multi-projects/libraries/{{projectName}}/{package.json.tmpl → package.json} +0 -0
  19. /package/templates/multi-projects/libraries/{{projectName}}/src/{index.test.ts.tmpl → index.test.ts} +0 -0
  20. /package/templates/single-project/.changeset/{config.json.tmpl → config.json} +0 -0
  21. /package/templates/single-project/.changeset/{welcome.md.tmpl → welcome.md} +0 -0
  22. /package/templates/single-project/.github/{PULL_REQUEST_TEMPLATE.md.tmpl → PULL_REQUEST_TEMPLATE.md} +0 -0
  23. /package/templates/single-project/{.nvmrc.tmpl → .nvmrc} +0 -0
  24. /package/templates/single-project/{CONTRIBUTING.md.tmpl → CONTRIBUTING.md} +0 -0
  25. /package/templates/single-project/{LICENSE.tmpl → LICENSE} +0 -0
  26. /package/templates/single-project/examples/default/{package.json.tmpl → package.json} +0 -0
  27. /package/templates/single-project/examples/default/src/{index.ts.tmpl → index.ts} +0 -0
  28. /package/templates/single-project/{{projectName}}/{README.md.tmpl → README.md} +0 -0
  29. /package/templates/single-project/{{projectName}}/{package.json.tmpl → package.json} +0 -0
  30. /package/templates/single-project/{{projectName}}/src/{index.test.ts.tmpl → index.test.ts} +0 -0
package/dist/index.js CHANGED
@@ -1,9 +1,8 @@
1
1
  import { helpers, termost } from "termost";
2
2
  import { createRequire } from "node:module";
3
- import { join, resolve } from "node:path";
4
- import { cpSync, existsSync, readFileSync, readdirSync, renameSync, writeFileSync } from "node:fs";
5
- import { chmod, mkdir, rm, symlink, writeFile } from "node:fs/promises";
6
- import { fdir } from "fdir";
3
+ import { basename, join, resolve } from "node:path";
4
+ import { existsSync, readdirSync } from "node:fs";
5
+ import { chmod, cp, mkdir, readFile, readdir, rename, rm, symlink, writeFile } from "node:fs/promises";
7
6
 
8
7
  //#region src/helpers.ts
9
8
  const require = createRequire(import.meta.url);
@@ -49,20 +48,20 @@ ${input.body}
49
48
  * @param path - The relative path.
50
49
  * @returns The resolved absolute path.
51
50
  * @example
52
- * resolveFromProjectDirectory(".gitignore");
51
+ * resolveFromWorkingDirectory(".gitignore");
53
52
  */
54
- const resolveFromProjectDirectory = (path) => {
55
- return resolve(process.cwd(), path);
53
+ const resolveFromWorkingDirectory = (...path) => {
54
+ return resolve(process.cwd(), ...path);
56
55
  };
57
56
  /**
58
57
  * Resolve a relative path to an absolute one resolved from the `stack` node module directory.
59
58
  * @param path - The relative path.
60
59
  * @returns The resolved absolute path.
61
60
  * @example
62
- * resolveFromStackDirectory("./templates");
61
+ * resolveFromPackageDirectory("./templates");
63
62
  */
64
- const resolveFromStackDirectory = (path) => {
65
- return resolve(import.meta.dirname, "../", path);
63
+ const resolveFromPackageDirectory = (...path) => {
64
+ return resolve(import.meta.dirname, "../", ...path);
66
65
  };
67
66
  const createError = (bin, error) => {
68
67
  const errorMessage = `\`${bin}\` command failed.\n${String(error)}`;
@@ -72,7 +71,7 @@ const createError = (bin, error) => {
72
71
  }
73
72
  return new Error(errorMessage);
74
73
  };
75
- const getNpmVersion = async () => {
74
+ const getPnpmVersion = async () => {
76
75
  try {
77
76
  return await helpers.exec("pnpm -v");
78
77
  } catch {
@@ -90,7 +89,7 @@ const setPackageManager = async () => {
90
89
  * Corepack is downloaded remotely to get always up-to-date npm registry fingerprints since they're hardcoded.
91
90
  * @see {@link https://github.com/nodejs/corepack/issues/613}
92
91
  */
93
- return helpers.exec("npx corepack enable");
92
+ return helpers.exec("pnx corepack enable");
94
93
  };
95
94
  const request = { async get(url, responseType) {
96
95
  const response = await fetch(url);
@@ -112,7 +111,7 @@ const eslint = (options) => async (files = []) => {
112
111
  const arguments_ = [
113
112
  ...eslintFiles,
114
113
  "--cache",
115
- `--cache-location ${resolveFromProjectDirectory("node_modules/.cache/.eslintcache")}`,
114
+ `--cache-location ${resolveFromWorkingDirectory("node_modules/.cache/.eslintcache")}`,
116
115
  "--no-error-on-unmatched-pattern"
117
116
  ];
118
117
  if (options.isFixMode) arguments_.push("--fix");
@@ -382,7 +381,7 @@ const createCleanCommand = (program) => {
382
381
  async handler() {
383
382
  const cachePath = "node_modules/.cache";
384
383
  const files = await retrieveIgnoredFiles();
385
- if (isDirectoryExistAndNotEmpty(resolveFromProjectDirectory(cachePath))) files.push(cachePath);
384
+ if (isDirectoryExistAndNotEmpty(resolveFromWorkingDirectory(cachePath))) files.push(cachePath);
386
385
  return files;
387
386
  },
388
387
  key: "files",
@@ -428,7 +427,7 @@ const PRESERVE_FILES = ["node_modules"];
428
427
 
429
428
  //#endregion
430
429
  //#region package.json
431
- var version = "2.39.0";
430
+ var version = "2.40.0";
432
431
 
433
432
  //#endregion
434
433
  //#region src/commands/create.ts
@@ -439,12 +438,12 @@ const createCreateCommand = (program) => {
439
438
  }).task({ handler() {
440
439
  botMessage({
441
440
  description: "I can guarantee you a project creation in under 1 minute 🚀",
442
- title: `I'm Stack v${version}, your bot assistant`,
441
+ title: `I'm Stack v${version} 👋`,
443
442
  type: "information"
444
443
  });
445
444
  } }).task({
446
445
  async handler() {
447
- await getNpmVersion();
446
+ await getPnpmVersion();
448
447
  },
449
448
  label: label$2("Check pre-requisites")
450
449
  }).input({
@@ -467,61 +466,92 @@ const createCreateCommand = (program) => {
467
466
  options: ["single-project", "multi-projects"],
468
467
  type: "select"
469
468
  }).task({
470
- async handler({ inputDescription, inputName, inputUrl }) {
469
+ async handler({ inputDescription, inputName, inputTemplate, inputUrl }) {
471
470
  if (!inputName) throw createError("stack create", "The project name is not optional. Make sure to provide a valid value (non-empty string).");
472
471
  const { repoName, repoOwner } = (inputUrl.startsWith("git") ? /^git@.*:(?<repoOwner>.*)\/(?<repoName>.*)\.git$/ : /^https?:\/\/.*\/(?<repoOwner>.*)\/(?<repoName>.*)\.git$/).exec(inputUrl)?.groups ?? {};
473
472
  if (!repoOwner || !repoName) throw createError("git", "The owner and repository name can not be extracted. Please make sure to follow either `/^git@.*:(?<repoOwner>.*)/(?<repoName>.*).git$/` or `/^https?://.*/(?<repoOwner>.*)/(?<repoName>.*).git$/` pattern.");
474
473
  const nodeVersion = await request.get("https://resolve-node.vercel.app/lts", "text");
475
- const { version: npmVersion } = await request.get("https://registry.npmjs.org/pnpm/latest", "json");
474
+ const { version: pnpmVersion } = await request.get("https://registry.npmjs.org/pnpm/latest", "json");
475
+ const projectName = slugify(inputName);
476
476
  return {
477
477
  licenseYear: (/* @__PURE__ */ new Date()).getFullYear().toString(),
478
478
  nodeVersion: nodeVersion.replace("v", ""),
479
- npmVersion: String(npmVersion),
480
- projectDescription: inputDescription.charAt(0).toUpperCase() + inputDescription.slice(1),
481
- projectName: inputName.toLowerCase(),
479
+ pnpmVersion: String(pnpmVersion),
480
+ projectDescription: toCapitalLetter(inputDescription),
481
+ projectName,
482
482
  projectUrl: inputUrl,
483
- repoId: `${repoOwner}/${repoName}`
483
+ repoId: `${repoOwner}/${repoName}`,
484
+ templatePath: resolveFromPackageDirectory("templates", inputTemplate),
485
+ workingPath: resolveFromWorkingDirectory(projectName)
484
486
  };
485
487
  },
486
488
  key: "data",
487
489
  label: label$2("Check and format input")
488
- }).task({
489
- async handler({ data }) {
490
- const projectPath = resolve(process.cwd(), data.projectName);
491
- await mkdir(projectPath);
492
- process.chdir(projectPath);
490
+ }).input({
491
+ defaultValue: true,
492
+ key: "canRemoveExistingDirectoryInput",
493
+ label({ data: { projectName } }) {
494
+ return label$2(`\`${projectName}\` directory already exists, do you want to remove it?`);
493
495
  },
494
- label({ data }) {
495
- return label$2(`Create \`${data.projectName}\` folder`);
496
+ skip({ data: { workingPath } }) {
497
+ return !existsSync(workingPath);
498
+ },
499
+ type: "confirm",
500
+ validate({ canRemoveExistingDirectoryInput, data: { projectName } }) {
501
+ if (canRemoveExistingDirectoryInput) return;
502
+ return createError("mkdir", `Remove or rename the \`${projectName}\` existing directory to apply the template from a clean state.`);
496
503
  }
497
504
  }).task({
498
- async handler({ data }) {
499
- await helpers.exec("git init");
500
- await helpers.exec(`git remote add origin ${data.projectUrl}`);
505
+ async handler({ canRemoveExistingDirectoryInput, data: { licenseYear, nodeVersion, pnpmVersion, projectDescription, projectName, projectUrl, repoId, templatePath, workingPath }, inputTemplate }) {
506
+ if (canRemoveExistingDirectoryInput) await rm(workingPath, {
507
+ force: true,
508
+ recursive: true
509
+ });
510
+ return createTemplateEngine(workingPath, {
511
+ projectName,
512
+ templateModel: {
513
+ licenseYear,
514
+ nodeVersion,
515
+ pnpmVersion,
516
+ projectDescription,
517
+ projectName,
518
+ projectUrl,
519
+ repoId
520
+ },
521
+ templateName: inputTemplate,
522
+ templatePath
523
+ });
501
524
  },
502
- label: label$2("Initialize `git`")
525
+ key: "templateEngine",
526
+ label({ data: { projectName }, inputTemplate }) {
527
+ return label$2(`Copy \`${inputTemplate}\` template to \`${projectName}\` directory`);
528
+ }
503
529
  }).task({
504
- handler({ data, inputTemplate }) {
505
- applyTemplate(inputTemplate, data);
530
+ async handler({ templateEngine }) {
531
+ await templateEngine.processContents();
532
+ await templateEngine.processPaths();
506
533
  },
507
- label: label$2("Apply template")
534
+ label() {
535
+ return label$2("Process template");
536
+ }
508
537
  }).task({
509
- async handler({ data: { projectName }, inputTemplate }) {
510
- await symlink(join(inputTemplate === "single-project" ? projectName : join("libraries", projectName), "README.md"), "./README.md");
538
+ async handler({ data: { projectUrl } }) {
539
+ await helpers.exec("git init");
540
+ await helpers.exec(`git remote add origin ${projectUrl}`);
511
541
  },
512
- label: label$2("Create a symlink to `README.md` file")
542
+ label: label$2("Initialize `git`")
513
543
  }).task({
514
544
  async handler() {
515
545
  await setPackageManager();
516
546
  },
517
547
  label: label$2("Set up the package manager")
518
548
  }).task({
519
- async handler({ data }) {
549
+ async handler({ data: { projectName } }) {
520
550
  const localDevelopmentDependencies = ["quickbundle", "vitest"];
521
551
  const globalDevelopmentDependencies = ["@adbayb/stack"];
522
552
  try {
523
553
  await helpers.exec(`pnpm add ${globalDevelopmentDependencies.join(" ")} --save-dev --ignore-workspace-root-check`);
524
- await helpers.exec(`pnpm add ${localDevelopmentDependencies.join(" ")} --save-dev --filter ${data.projectName}`);
554
+ await helpers.exec(`pnpm add ${localDevelopmentDependencies.join(" ")} --save-dev --filter ${projectName}`);
525
555
  await helpers.exec("pnpm install");
526
556
  } catch (error) {
527
557
  throw createError("pnpm", error);
@@ -539,53 +569,95 @@ const createCreateCommand = (program) => {
539
569
  await helpers.exec("git commit -m \"chore: initial commit\"");
540
570
  },
541
571
  label: label$2("Commit")
542
- }).task({ handler({ data }) {
572
+ }).task({ handler({ data: { projectName } }) {
543
573
  botMessage({
544
- description: `Run \`cd ./${data.projectName}\` and Enjoy 🚀`,
574
+ description: `Run \`cd ./${projectName}\` and Enjoy 🚀`,
545
575
  title: "The project was successfully created",
546
576
  type: "success"
547
577
  });
548
578
  } });
549
579
  };
550
580
  const label$2 = (message) => `${message} 🔨`;
551
- /**
552
- * A simple template engine to evaluate dynamic expressions and apply side effets (such as hydrating a content with values from an input object) on impacted template files.
553
- * @param template - The selected template.
554
- * @param dataModel - Data model mapping the template expression key with its corresponding value.
555
- * @example
556
- * applyTemplate(
557
- * { toReplace: "value" },
558
- * );
559
- */
560
- const applyTemplate = (template, dataModel) => {
561
- const templateExtension = ".tmpl";
562
- const templateRootPath = resolveFromStackDirectory(join("./templates", template));
563
- const projectRootPath = resolveFromProjectDirectory("./");
564
- const templateExpressionRegExp = /{{(.*?)}}/g;
565
- const evaluate = (content) => {
566
- return content.replaceAll(templateExpressionRegExp, (_, key) => dataModel[key] || "");
567
- };
568
- /** Copy the template before mutations. */
569
- cpSync(templateRootPath, projectRootPath, {
581
+ const slugify = (input) => {
582
+ return input.toLowerCase().replaceAll(/[^a-z0-9\s-]/g, "").trim().replaceAll(/\s+/g, "-").replaceAll(/-+/g, "-");
583
+ };
584
+ const toCapitalLetter = (input) => {
585
+ return input.charAt(0).toUpperCase() + input.slice(1);
586
+ };
587
+ const createTemplateEngine = async (workingPath, { projectName, templateModel, templateName, templatePath }) => {
588
+ if (!existsSync(workingPath)) await mkdir(workingPath);
589
+ await cp(templatePath, workingPath, {
570
590
  force: true,
571
591
  recursive: true
572
592
  });
573
- /** Template file mutations. */
574
- new fdir().withBasePath().glob(`**/*${templateExtension}`).crawl(projectRootPath).sync().forEach((templateFilePath) => {
575
- const projectFilePath = templateFilePath.slice(0, templateFilePath.lastIndexOf(templateExtension));
576
- const content = evaluate(readFileSync(templateFilePath, "utf8"));
577
- renameSync(templateFilePath, projectFilePath);
578
- writeFileSync(projectFilePath, content, "utf8");
593
+ const gitignoreFile = join(workingPath, ".gitignore.tmpl");
594
+ if (existsSync(gitignoreFile)) await rename(gitignoreFile, join(workingPath, ".gitignore"));
595
+ const eslintConfigFile = join(workingPath, "eslint.config.js.tmpl");
596
+ if (existsSync(eslintConfigFile)) await rename(eslintConfigFile, join(workingPath, "eslint.config.js"));
597
+ const templateEntries = await getTemplateEntries(workingPath);
598
+ process.chdir(workingPath);
599
+ return {
600
+ async processContents() {
601
+ await Promise.all(templateEntries.filter(({ type }) => type === "content").map(async (entry) => {
602
+ return writeFile(entry.path, setTemplateVariables(entry, templateModel));
603
+ }));
604
+ },
605
+ async processPaths() {
606
+ const sortedDirectoryEntries = templateEntries.filter(({ content, type }) => {
607
+ if (type === "path.directory" || type === "path.file") return hasTemplateVariable(basename(content));
608
+ return false;
609
+ }).toSorted(({ path: pathA }, { path: pathB }) => pathB.length - pathA.length);
610
+ for (const entry of sortedDirectoryEntries) {
611
+ const newPath = setTemplateVariables(entry, templateModel);
612
+ await rename(entry.path, newPath);
613
+ }
614
+ await symlink(join(templateName === "single-project" ? projectName : join("libraries", projectName), "README.md"), "./README.md");
615
+ }
616
+ };
617
+ };
618
+ const getTemplateEntries = async (path) => {
619
+ const entries = await readdir(path, {
620
+ recursive: true,
621
+ withFileTypes: true
579
622
  });
580
- /** Template folder mutations. */
581
- new fdir().withBasePath().onlyDirs().filter((path) => {
582
- return templateExpressionRegExp.test(path);
583
- }).crawl(projectRootPath).sync().toSorted((a, b) => b.length - a.length).forEach((templateFolderPath) => {
584
- renameSync(templateFolderPath, templateFolderPath.replaceAll(templateExpressionRegExp, (_, dataModelKey) => {
585
- return dataModel[dataModelKey];
586
- }));
623
+ return (await Promise.all(entries.map(async (entry) => {
624
+ const isDirectory = entry.isDirectory();
625
+ if (!isDirectory && !entry.isFile()) return [];
626
+ const entryPath = resolve(entry.parentPath, entry.name);
627
+ return (isDirectory ? [{
628
+ content: entryPath,
629
+ type: "path.directory"
630
+ }] : [{
631
+ content: entryPath,
632
+ type: "path.file"
633
+ }, {
634
+ content: await readFile(entryPath, "utf8"),
635
+ type: "content"
636
+ }]).map(({ content, type }) => {
637
+ if (!hasTemplateVariable(content)) return void 0;
638
+ return {
639
+ content,
640
+ path: entryPath,
641
+ type
642
+ };
643
+ }).filter((input) => Boolean(input));
644
+ }))).flat();
645
+ };
646
+ const setTemplateVariables = (entry, model) => {
647
+ return entry.content.replaceAll(TEMPLATE_VARIABLE_MATCHER, (match, dataModelKey) => {
648
+ return model[dataModelKey] ?? match;
587
649
  });
588
650
  };
651
+ const TEMPLATE_VARIABLE_MATCHER = /* @__PURE__ */ new RegExp(/{{(.*?)}}/g, "gi");
652
+ const hasTemplateVariable = (input) => {
653
+ /**
654
+ * TemplateVariableMatcher.test() is not used since the `RegExp` is stateful when the global is used leading to some unstable results
655
+ * (relying on latest `lastIndex` set (lastIndex specifies the index at which to start the next match)).
656
+ * String.search is stateless.
657
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test MDN documentation}.
658
+ */
659
+ return input.search(TEMPLATE_VARIABLE_MATCHER) >= 0;
660
+ };
589
661
 
590
662
  //#endregion
591
663
  //#region src/commands/fix/fixFormatting.ts
@@ -600,7 +672,7 @@ const fixFormatting = async (files) => {
600
672
  if (prettierFiles.length === 0) return;
601
673
  }
602
674
  const arguments_ = [...prettierFiles];
603
- if (existsSync(resolveFromProjectDirectory(".gitignore"))) arguments_.push("--ignore-path .gitignore");
675
+ if (existsSync(resolveFromWorkingDirectory(".gitignore"))) arguments_.push("--ignore-path .gitignore");
604
676
  arguments_.push("--write", "--ignore-unknown", "--no-error-on-unmatched-pattern");
605
677
  try {
606
678
  return await helpers.exec(`prettier ${arguments_.join(" ")}`);
@@ -663,7 +735,7 @@ const createInstallCommand = (program) => {
663
735
  };
664
736
  const label = (message) => `${message} 📲`;
665
737
  const installGitHook = async (hook, content) => {
666
- const filename = resolveFromProjectDirectory(`.git/hooks/${hook}`);
738
+ const filename = resolveFromWorkingDirectory(`.git/hooks/${hook}`);
667
739
  await writeFile(filename, content);
668
740
  return chmod(filename, "0755");
669
741
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adbayb/stack",
3
- "version": "2.39.0",
3
+ "version": "2.40.0",
4
4
  "description": "My opinionated JavaScript-based toolchain",
5
5
  "keywords": [
6
6
  "stack",
@@ -59,7 +59,6 @@
59
59
  "eslint-plugin-prettier": "^5.5.6",
60
60
  "eslint-plugin-sonarjs": "^4.1.0",
61
61
  "eslint-plugin-unicorn": "^68.0.0",
62
- "fdir": "^6.5.0",
63
62
  "globals": "^17.6.0",
64
63
  "prettier": "^3.8.4",
65
64
  "prettier-plugin-packagejson": "^3.0.2",
@@ -17,18 +17,13 @@
17
17
  "watch": "stack watch"
18
18
  },
19
19
  "prettier": "@adbayb/stack/prettier",
20
- "packageManager": "pnpm@{{npmVersion}}",
20
+ "packageManager": "pnpm@{{pnpmVersion}}",
21
21
  "devEngines": {
22
- "packageManager": [
23
- {
24
- "name": "pnpm",
25
- "version": "{{npmVersion}}",
26
- "onFail": "download"
27
- },
28
- {
29
- "name": "npm"
30
- }
31
- ],
22
+ "packageManager": {
23
+ "name": "pnpm",
24
+ "version": "{{pnpmVersion}}",
25
+ "onFail": "download"
26
+ },
32
27
  "runtime": {
33
28
  "name": "node",
34
29
  "version": ">=24.0.0",
@@ -1,3 +1,6 @@
1
+ allowBuilds:
2
+ esbuild: true
3
+ unrs-resolver: true
1
4
  minimumReleaseAgeExclude:
2
5
  - "@adbayb/*"
3
6
  packages:
@@ -13,3 +16,5 @@ publicHoistPattern:
13
16
  - "*turbo*"
14
17
  - "*typescript*"
15
18
  - "*types*"
19
+ saveExact: true
20
+ verifyDepsBeforeRun: "warn"
@@ -17,18 +17,13 @@
17
17
  "watch": "stack watch"
18
18
  },
19
19
  "prettier": "@adbayb/stack/prettier",
20
- "packageManager": "pnpm@{{npmVersion}}",
20
+ "packageManager": "pnpm@{{pnpmVersion}}",
21
21
  "devEngines": {
22
- "packageManager": [
23
- {
24
- "name": "pnpm",
25
- "version": "{{npmVersion}}",
26
- "onFail": "download"
27
- },
28
- {
29
- "name": "npm"
30
- }
31
- ],
22
+ "packageManager": {
23
+ "name": "pnpm",
24
+ "version": "{{pnpmVersion}}",
25
+ "onFail": "download"
26
+ },
32
27
  "runtime": {
33
28
  "name": "node",
34
29
  "version": ">=24.0.0",
@@ -1,3 +1,6 @@
1
+ allowBuilds:
2
+ esbuild: true
3
+ unrs-resolver: true
1
4
  minimumReleaseAgeExclude:
2
5
  - "@adbayb/*"
3
6
  packages:
@@ -12,3 +15,5 @@ publicHoistPattern:
12
15
  - "*turbo*"
13
16
  - "*typescript*"
14
17
  - "*types*"
18
+ saveExact: true
19
+ verifyDepsBeforeRun: "warn"
@@ -1 +0,0 @@
1
- save-exact=true
@@ -1 +0,0 @@
1
- save-exact=true