@rootnative/cli 0.0.0-alpha.7 → 0.0.0-alpha.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/README.md CHANGED
@@ -1,3 +1,6 @@
1
+ <!-- Absolute URL: npm does not resolve repository-relative image paths. -->
2
+ <img src="https://raw.githubusercontent.com/rootnative/ui/main/assets/brand/rootnative-mark.png" alt="" width="88" height="88" />
3
+
1
4
  # rootnative
2
5
 
3
6
  CLI for adding [RootNative UI](https://github.com/rootnative/ui) components directly into your React Native or Expo project. Inspired by [shadcn/ui](https://ui.shadcn.com) — you own the code.
package/dist/index.mjs CHANGED
@@ -697,6 +697,46 @@ function isValidTemplate(value) {
697
697
  function slugify(input) {
698
698
  return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
699
699
  }
700
+ function isCurrentDirName(input) {
701
+ const trimmed = input.trim();
702
+ return trimmed === "." || trimmed === "./" || trimmed === ".\\";
703
+ }
704
+ function resolveProjectTarget(input, cwd) {
705
+ const currentDir = path7.resolve(cwd);
706
+ if (isCurrentDirName(input)) {
707
+ const projectName2 = slugify(path7.basename(currentDir));
708
+ if (!projectName2) return { ok: false, reason: "invalid-folder-name" };
709
+ return {
710
+ ok: true,
711
+ target: { projectName: projectName2, targetDir: currentDir, useCurrentDir: true }
712
+ };
713
+ }
714
+ const projectName = slugify(input);
715
+ if (!projectName) return { ok: false, reason: "invalid-name" };
716
+ const targetDir = path7.resolve(currentDir, projectName);
717
+ return {
718
+ ok: true,
719
+ target: {
720
+ projectName,
721
+ targetDir,
722
+ useCurrentDir: targetDir === currentDir
723
+ }
724
+ };
725
+ }
726
+ function templateFiles(templateName) {
727
+ return [
728
+ ...TEMPLATE_CONFIGS[templateName].textFiles,
729
+ ...TEMPLATE_OPTIONAL_TEXT_FILES,
730
+ ...TEMPLATE_BINARY_FILES
731
+ ];
732
+ }
733
+ async function findConflicts(dir, files) {
734
+ const conflicts = [];
735
+ for (const file of files) {
736
+ if (await fs5.pathExists(path7.join(dir, file))) conflicts.push(file);
737
+ }
738
+ return conflicts;
739
+ }
700
740
  function toDisplayName(slug) {
701
741
  return slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
702
742
  }
@@ -763,9 +803,9 @@ async function createCommand(name, options = {}) {
763
803
  }
764
804
  templateName = value;
765
805
  }
766
- let projectName;
806
+ let nameInput;
767
807
  if (name) {
768
- projectName = slugify(name);
808
+ nameInput = name;
769
809
  } else {
770
810
  const { value } = await prompts2({
771
811
  type: "text",
@@ -778,8 +818,26 @@ async function createCommand(name, options = {}) {
778
818
  logger.info("Create cancelled.");
779
819
  return;
780
820
  }
781
- projectName = slugify(value);
821
+ nameInput = value;
822
+ }
823
+ const resolved = resolveProjectTarget(nameInput, process.cwd());
824
+ if (!resolved.ok) {
825
+ if (resolved.reason === "invalid-folder-name") {
826
+ logger.error(
827
+ `Cannot make a project name from the folder ${chalk3.bold(
828
+ path7.basename(path7.resolve(process.cwd()))
829
+ )}.`
830
+ );
831
+ logger.info(
832
+ `Give a name instead: ${chalk3.bold("npx rootnative create my-app")}`
833
+ );
834
+ } else {
835
+ logger.error(`${chalk3.bold(nameInput)} is not a usable project name.`);
836
+ logger.info("Use letters and numbers, for example my-app.");
837
+ }
838
+ process.exit(1);
782
839
  }
840
+ const { projectName, targetDir, useCurrentDir } = resolved.target;
783
841
  let displayName;
784
842
  if (options.yes) {
785
843
  displayName = toDisplayName(projectName);
@@ -820,8 +878,34 @@ async function createCommand(name, options = {}) {
820
878
  }
821
879
  packageManager = value;
822
880
  }
823
- const targetDir = path7.resolve(process.cwd(), projectName);
824
- if (await fs5.pathExists(targetDir)) {
881
+ if (useCurrentDir) {
882
+ const conflicts = await findConflicts(
883
+ targetDir,
884
+ templateFiles(templateName)
885
+ );
886
+ if (conflicts.length > 0) {
887
+ logger.warn("These files in the current directory will be overwritten:");
888
+ for (const file of conflicts) {
889
+ logger.info(` ${file}`);
890
+ }
891
+ logger.break();
892
+ if (options.yes) {
893
+ logger.error("Nothing was changed.");
894
+ logger.info("Move or delete these files, then run create again.");
895
+ process.exit(1);
896
+ }
897
+ const { overwrite } = await prompts2({
898
+ type: "confirm",
899
+ name: "overwrite",
900
+ message: "Overwrite them?",
901
+ initial: false
902
+ });
903
+ if (!overwrite) {
904
+ logger.info("Create cancelled.");
905
+ return;
906
+ }
907
+ }
908
+ } else if (await fs5.pathExists(targetDir)) {
825
909
  if (options.yes) {
826
910
  logger.warn(`Directory ${chalk3.bold(projectName)} already exists.`);
827
911
  process.exit(1);
@@ -829,7 +913,7 @@ async function createCommand(name, options = {}) {
829
913
  const { overwrite } = await prompts2({
830
914
  type: "confirm",
831
915
  name: "overwrite",
832
- message: `Directory ${chalk3.bold(projectName)} already exists. Overwrite?`,
916
+ message: `Directory ${chalk3.bold(projectName)} already exists. Delete it and all of its contents?`,
833
917
  initial: false
834
918
  });
835
919
  if (!overwrite) {
@@ -913,7 +997,9 @@ async function createCommand(name, options = {}) {
913
997
  logger.break();
914
998
  logger.error("Failed to install dependencies");
915
999
  logger.info(
916
- `Run manually: ${chalk3.bold(`cd ${projectName} && ${installCmd}`)}`
1000
+ `Run manually: ${chalk3.bold(
1001
+ useCurrentDir ? installCmd : `cd ${projectName} && ${installCmd}`
1002
+ )}`
917
1003
  );
918
1004
  }
919
1005
  }
@@ -921,7 +1007,9 @@ async function createCommand(name, options = {}) {
921
1007
  logger.success(`Project ${chalk3.bold(displayName)} is ready!`);
922
1008
  logger.break();
923
1009
  logger.info("Next steps:");
924
- logger.info(` cd ${projectName}`);
1010
+ if (!useCurrentDir) {
1011
+ logger.info(` cd ${projectName}`);
1012
+ }
925
1013
  if (!shouldInstall) {
926
1014
  logger.info(` ${getInstallCommand2(packageManager)}`);
927
1015
  }
@@ -2006,10 +2094,26 @@ function isValidPackageManager(value) {
2006
2094
  return PACKAGE_MANAGERS.includes(value);
2007
2095
  }
2008
2096
 
2097
+ // src/lib/version.ts
2098
+ import { createRequire } from "module";
2099
+ var UNKNOWN_VERSION = "0.0.0-unknown";
2100
+ var PACKAGE_JSON_CANDIDATES = ["../package.json", "../../package.json"];
2101
+ function getCliVersion() {
2102
+ const require2 = createRequire(import.meta.url);
2103
+ for (const candidate of PACKAGE_JSON_CANDIDATES) {
2104
+ try {
2105
+ const pkg = require2(candidate);
2106
+ if (pkg.name === "@rootnative/cli" && pkg.version) return pkg.version;
2107
+ } catch {
2108
+ }
2109
+ }
2110
+ return UNKNOWN_VERSION;
2111
+ }
2112
+
2009
2113
  // src/index.ts
2010
2114
  var program = new Command();
2011
- program.name("rootnative").description("Add RootNative UI components to your React Native project").version("0.1.0");
2012
- program.command("create").description("Create a new project with RootNative UI pre-configured").argument("[name]", "Project name").option("-y, --yes", "Skip prompts and use defaults", false).option("-t, --template <name>", "Template to use (blank, with-router)").option(
2115
+ program.name("rootnative").description("Add RootNative UI components to your React Native project").version(getCliVersion());
2116
+ program.command("create").description("Create a new project with RootNative UI pre-configured").argument("[name]", 'Project name, or "." to use the current directory').option("-y, --yes", "Skip prompts and use defaults", false).option("-t, --template <name>", "Template to use (blank, with-router)").option(
2013
2117
  "--package-manager <pm>",
2014
2118
  `Package manager to use (${PACKAGE_MANAGERS.join(", ")})`
2015
2119
  ).action(async (name, options) => {
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # @rootnative/cli — CLI for RootNative UI
2
2
 
3
- > Version: 0.0.0-alpha.7
3
+ > Version: 0.0.0-alpha.9
4
4
  > Binary: `rootnative`
5
5
  > Requirements: Node >=18
6
6
 
@@ -15,9 +15,12 @@ Create a new project with RootNative UI pre-configured. Fetches the quickstart t
15
15
  ```bash
16
16
  npx rootnative create # Interactive
17
17
  npx rootnative create my-app # With name
18
+ npx rootnative create . # Into the current directory, named after it
18
19
  npx rootnative create my-app -y # Non-interactive, accept defaults
19
20
  ```
20
21
 
22
+ Pass `.` to scaffold into the directory you are already in. The project name comes from that directory's name, and the directory is never deleted — the CLI lists any file the template would overwrite and asks first (with `-y` it stops and changes nothing). A named project goes into a new subdirectory, and the CLI asks before it deletes an existing one.
23
+
21
24
  Options:
22
25
  - `-y, --yes` — Skip prompts and use defaults
23
26
  - `-t, --template <name>` — Template to use (`blank`, `with-router`)
@@ -47,7 +50,7 @@ Creates `rootnative.json`:
47
50
  "$schema": "https://rootnative.github.io/ui/schema.json",
48
51
  "aliases": { "components": "@/components/ui", "lib": "@/lib" },
49
52
  "registryUrl": "https://raw.githubusercontent.com/rootnative/ui",
50
- "registryVersion": "v0.0.0-alpha.7"
53
+ "registryVersion": "v0.0.0-alpha.9"
51
54
  }
52
55
  ```
53
56
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rootnative/cli",
3
- "version": "0.0.0-alpha.7",
3
+ "version": "0.0.0-alpha.9",
4
4
  "description": "CLI for adding RootNative UI components to your React Native project",
5
5
  "private": false,
6
6
  "bin": {