@rootnative/cli 0.0.0-alpha.0 → 0.0.0-alpha.2
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 +6 -0
- package/dist/index.mjs +131 -44
- package/llms.txt +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -305,6 +305,12 @@ export default function App() {
|
|
|
305
305
|
}
|
|
306
306
|
```
|
|
307
307
|
|
|
308
|
+
## Docs
|
|
309
|
+
|
|
310
|
+
Full docs: https://rootnative.github.io/ui/cli
|
|
311
|
+
|
|
312
|
+
LLM-optimized reference: https://rootnative.github.io/ui/llms-full.txt — or read `node_modules/@rootnative/cli/llms.txt` for the exact installed version.
|
|
313
|
+
|
|
308
314
|
## License
|
|
309
315
|
|
|
310
316
|
MIT
|
package/dist/index.mjs
CHANGED
|
@@ -277,6 +277,10 @@ function transformImports(source, options) {
|
|
|
277
277
|
if (importPath.startsWith("@rootnative/core")) {
|
|
278
278
|
return match;
|
|
279
279
|
}
|
|
280
|
+
if (importPath.startsWith("../internal/")) {
|
|
281
|
+
const restOfPath = importPath.replace(/^\.\.\/internal\//, "");
|
|
282
|
+
return `${prefix}${quote}./${restOfPath}${quote}`;
|
|
283
|
+
}
|
|
280
284
|
if (importPath.startsWith("../")) {
|
|
281
285
|
const targetComponent = extractComponentName(importPath);
|
|
282
286
|
if (targetComponent && installedComponents.includes(targetComponent)) {
|
|
@@ -561,6 +565,7 @@ var TEMPLATE_BINARY_FILES = [
|
|
|
561
565
|
"assets/adaptive-icon.png",
|
|
562
566
|
"assets/favicon.png"
|
|
563
567
|
];
|
|
568
|
+
var TEMPLATE_OPTIONAL_TEXT_FILES = ["CLAUDE.md"];
|
|
564
569
|
function isValidTemplate(value) {
|
|
565
570
|
return value in TEMPLATE_CONFIGS;
|
|
566
571
|
}
|
|
@@ -589,6 +594,11 @@ async function fetchText(url) {
|
|
|
589
594
|
}
|
|
590
595
|
return res.text();
|
|
591
596
|
}
|
|
597
|
+
async function fetchTextOptional(url) {
|
|
598
|
+
const res = await fetch(url);
|
|
599
|
+
if (!res.ok) return null;
|
|
600
|
+
return res.text();
|
|
601
|
+
}
|
|
592
602
|
async function fetchBinary(url) {
|
|
593
603
|
const res = await fetch(url);
|
|
594
604
|
if (!res.ok) return null;
|
|
@@ -737,6 +747,12 @@ async function createCommand(name, options = {}) {
|
|
|
737
747
|
}
|
|
738
748
|
await fs4.outputFile(path4.join(targetDir, file), content);
|
|
739
749
|
}
|
|
750
|
+
for (const file of TEMPLATE_OPTIONAL_TEXT_FILES) {
|
|
751
|
+
const content = await fetchTextOptional(`${templateBaseUrl}/${file}`);
|
|
752
|
+
if (content !== null) {
|
|
753
|
+
await fs4.outputFile(path4.join(targetDir, file), content);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
740
756
|
for (const file of TEMPLATE_BINARY_FILES) {
|
|
741
757
|
const buffer = await fetchBinary(`${templateBaseUrl}/${file}`);
|
|
742
758
|
if (buffer) {
|
|
@@ -912,6 +928,22 @@ async function doctorCommand(cwd) {
|
|
|
912
928
|
}
|
|
913
929
|
}
|
|
914
930
|
const nodeModules = path5.resolve(cwd, "node_modules");
|
|
931
|
+
const inertiaPkgPath = path5.join(
|
|
932
|
+
nodeModules,
|
|
933
|
+
"@rootnative",
|
|
934
|
+
"inertia",
|
|
935
|
+
"package.json"
|
|
936
|
+
);
|
|
937
|
+
if (await fs5.pathExists(inertiaPkgPath)) {
|
|
938
|
+
const inertiaPkg = await fs5.readJSON(inertiaPkgPath);
|
|
939
|
+
logCheck("pass", `@rootnative/inertia@${inertiaPkg.version} installed`);
|
|
940
|
+
} else {
|
|
941
|
+
logCheck(
|
|
942
|
+
"fail",
|
|
943
|
+
'@rootnative/inertia not installed (required by all animated components). Run "rootnative upgrade" or install it with react-native-reanimated and react-native-worklets.'
|
|
944
|
+
);
|
|
945
|
+
issues++;
|
|
946
|
+
}
|
|
915
947
|
const safeAreaInstalled = await fs5.pathExists(
|
|
916
948
|
path5.join(nodeModules, "react-native-safe-area-context")
|
|
917
949
|
);
|
|
@@ -947,6 +979,41 @@ async function doctorCommand(cwd) {
|
|
|
947
979
|
import chalk5 from "chalk";
|
|
948
980
|
import { execa as execa3 } from "execa";
|
|
949
981
|
import prompts3 from "prompts";
|
|
982
|
+
|
|
983
|
+
// src/lib/llm-docs.ts
|
|
984
|
+
import path6 from "path";
|
|
985
|
+
import fs6 from "fs-extra";
|
|
986
|
+
var CLAUDE_MD = "CLAUDE.md";
|
|
987
|
+
var POINTER_MARKER = "@rootnative/components/llms.txt";
|
|
988
|
+
var LLM_DOCS_POINTER = `## RootNative UI docs for AI agents
|
|
989
|
+
|
|
990
|
+
This project uses RootNative UI (\`@rootnative/*\`). LLM-optimized docs:
|
|
991
|
+
|
|
992
|
+
- \`node_modules/@rootnative/components/llms.txt\` \u2014 all component props for the exact installed version (works offline)
|
|
993
|
+
- \`node_modules/@rootnative/core/llms.txt\` \u2014 theme system API (\`ThemeProvider\`, \`useTheme\`, \`defineTheme\`)
|
|
994
|
+
- https://rootnative.github.io/ui/llms.txt \u2014 hosted overview (latest release)
|
|
995
|
+
- https://rootnative.github.io/ui/llms-full.txt \u2014 hosted complete API reference (latest release)
|
|
996
|
+
|
|
997
|
+
Prefer the \`node_modules\` copies \u2014 they match the installed version exactly.
|
|
998
|
+
`;
|
|
999
|
+
async function ensureLlmDocsPointer(cwd) {
|
|
1000
|
+
const filePath = path6.join(cwd, CLAUDE_MD);
|
|
1001
|
+
if (!await fs6.pathExists(filePath)) {
|
|
1002
|
+
await fs6.outputFile(filePath, `# CLAUDE.md
|
|
1003
|
+
|
|
1004
|
+
${LLM_DOCS_POINTER}`);
|
|
1005
|
+
return "created";
|
|
1006
|
+
}
|
|
1007
|
+
const existing = await fs6.readFile(filePath, "utf8");
|
|
1008
|
+
if (existing.includes(POINTER_MARKER)) {
|
|
1009
|
+
return "already-present";
|
|
1010
|
+
}
|
|
1011
|
+
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
1012
|
+
await fs6.outputFile(filePath, `${existing}${separator}${LLM_DOCS_POINTER}`);
|
|
1013
|
+
return "appended";
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// src/commands/init.ts
|
|
950
1017
|
async function initCommand(cwd, options = {}) {
|
|
951
1018
|
logger.break();
|
|
952
1019
|
if (await configExists(cwd)) {
|
|
@@ -1029,6 +1096,26 @@ async function initCommand(cwd, options = {}) {
|
|
|
1029
1096
|
};
|
|
1030
1097
|
await writeConfig(cwd, config);
|
|
1031
1098
|
logger.success("Created rootnative.json");
|
|
1099
|
+
let addLlmDocs = options.yes;
|
|
1100
|
+
if (!options.yes) {
|
|
1101
|
+
const answer = await prompts3({
|
|
1102
|
+
type: "confirm",
|
|
1103
|
+
name: "addLlmDocs",
|
|
1104
|
+
message: "Add RootNative LLM docs pointer to CLAUDE.md (for AI agents)?",
|
|
1105
|
+
initial: true
|
|
1106
|
+
});
|
|
1107
|
+
addLlmDocs = answer.addLlmDocs;
|
|
1108
|
+
}
|
|
1109
|
+
if (addLlmDocs) {
|
|
1110
|
+
const result = await ensureLlmDocsPointer(cwd);
|
|
1111
|
+
if (result === "created") {
|
|
1112
|
+
logger.success("Created CLAUDE.md with LLM docs pointer");
|
|
1113
|
+
} else if (result === "appended") {
|
|
1114
|
+
logger.success("Added LLM docs pointer to CLAUDE.md");
|
|
1115
|
+
} else {
|
|
1116
|
+
logger.info("CLAUDE.md already points to the RootNative LLM docs");
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1032
1119
|
let installCore = options.yes;
|
|
1033
1120
|
if (!options.yes) {
|
|
1034
1121
|
const answer = await prompts3({
|
|
@@ -1065,9 +1152,9 @@ async function initCommand(cwd, options = {}) {
|
|
|
1065
1152
|
}
|
|
1066
1153
|
|
|
1067
1154
|
// src/commands/list.ts
|
|
1068
|
-
import
|
|
1155
|
+
import path7 from "path";
|
|
1069
1156
|
import chalk6 from "chalk";
|
|
1070
|
-
import
|
|
1157
|
+
import fs7 from "fs-extra";
|
|
1071
1158
|
async function listCommand(cwd) {
|
|
1072
1159
|
logger.break();
|
|
1073
1160
|
const config = await readConfig(cwd);
|
|
@@ -1093,8 +1180,8 @@ async function listCommand(cwd) {
|
|
|
1093
1180
|
` ${chalk6.dim("-".repeat(70))}`
|
|
1094
1181
|
);
|
|
1095
1182
|
for (const component of registryIndex.components) {
|
|
1096
|
-
const componentDir =
|
|
1097
|
-
const installed = await
|
|
1183
|
+
const componentDir = path7.join(componentsDir, component.name);
|
|
1184
|
+
const installed = await fs7.pathExists(componentDir);
|
|
1098
1185
|
const status = installed ? chalk6.green("installed") : chalk6.dim("-");
|
|
1099
1186
|
const nameDisplay = installed ? chalk6.green(component.name) : component.name;
|
|
1100
1187
|
console.log(
|
|
@@ -1114,9 +1201,9 @@ function padEnd(str, length) {
|
|
|
1114
1201
|
}
|
|
1115
1202
|
|
|
1116
1203
|
// src/commands/update.ts
|
|
1117
|
-
import
|
|
1204
|
+
import path8 from "path";
|
|
1118
1205
|
import chalk8 from "chalk";
|
|
1119
|
-
import
|
|
1206
|
+
import fs8 from "fs-extra";
|
|
1120
1207
|
import prompts4 from "prompts";
|
|
1121
1208
|
|
|
1122
1209
|
// src/lib/diff.ts
|
|
@@ -1266,8 +1353,8 @@ async function updateCommand(componentNames, cwd, options) {
|
|
|
1266
1353
|
targetNames = componentNames;
|
|
1267
1354
|
const missing = [];
|
|
1268
1355
|
for (const name of targetNames) {
|
|
1269
|
-
const dir =
|
|
1270
|
-
if (!await
|
|
1356
|
+
const dir = path8.join(componentsDir, name);
|
|
1357
|
+
if (!await fs8.pathExists(dir)) {
|
|
1271
1358
|
missing.push(name);
|
|
1272
1359
|
}
|
|
1273
1360
|
}
|
|
@@ -1290,8 +1377,8 @@ async function updateCommand(componentNames, cwd, options) {
|
|
|
1290
1377
|
resolveSpinner.succeed("Registry fetched");
|
|
1291
1378
|
const componentDiffs = [];
|
|
1292
1379
|
for (const { entry } of resolution.components) {
|
|
1293
|
-
const componentDir =
|
|
1294
|
-
if (!await
|
|
1380
|
+
const componentDir = path8.join(componentsDir, entry.name);
|
|
1381
|
+
if (!await fs8.pathExists(componentDir)) {
|
|
1295
1382
|
componentDiffs.push({
|
|
1296
1383
|
name: entry.name,
|
|
1297
1384
|
diffs: [],
|
|
@@ -1301,8 +1388,8 @@ async function updateCommand(componentNames, cwd, options) {
|
|
|
1301
1388
|
}
|
|
1302
1389
|
const diffs = [];
|
|
1303
1390
|
for (const filePath of entry.files) {
|
|
1304
|
-
const fileName =
|
|
1305
|
-
const localPath =
|
|
1391
|
+
const fileName = path8.basename(filePath);
|
|
1392
|
+
const localPath = path8.join(componentDir, fileName);
|
|
1306
1393
|
const remoteContent = await fetchFileContent(config, filePath);
|
|
1307
1394
|
const transformed = transformImports(remoteContent, {
|
|
1308
1395
|
config,
|
|
@@ -1310,8 +1397,8 @@ async function updateCommand(componentNames, cwd, options) {
|
|
|
1310
1397
|
installedComponents: allComponentNames
|
|
1311
1398
|
});
|
|
1312
1399
|
let localContent = "";
|
|
1313
|
-
if (await
|
|
1314
|
-
localContent = await
|
|
1400
|
+
if (await fs8.pathExists(localPath)) {
|
|
1401
|
+
localContent = await fs8.readFile(localPath, "utf-8");
|
|
1315
1402
|
}
|
|
1316
1403
|
diffs.push(computeDiff(localContent, transformed, fileName));
|
|
1317
1404
|
}
|
|
@@ -1322,12 +1409,12 @@ async function updateCommand(componentNames, cwd, options) {
|
|
|
1322
1409
|
for (const utilName of resolution.utils) {
|
|
1323
1410
|
const utilEntry = utilsRegistry[utilName];
|
|
1324
1411
|
if (!utilEntry) continue;
|
|
1325
|
-
const fileName =
|
|
1326
|
-
const localPath =
|
|
1412
|
+
const fileName = path8.basename(utilEntry.file);
|
|
1413
|
+
const localPath = path8.join(libDir, fileName);
|
|
1327
1414
|
const remoteContent = await fetchFileContent(config, utilEntry.file);
|
|
1328
1415
|
let localContent = "";
|
|
1329
|
-
if (await
|
|
1330
|
-
localContent = await
|
|
1416
|
+
if (await fs8.pathExists(localPath)) {
|
|
1417
|
+
localContent = await fs8.readFile(localPath, "utf-8");
|
|
1331
1418
|
}
|
|
1332
1419
|
utilDiffs.push(computeDiff(localContent, remoteContent, `lib/${fileName}`));
|
|
1333
1420
|
}
|
|
@@ -1339,10 +1426,10 @@ async function updateCommand(componentNames, cwd, options) {
|
|
|
1339
1426
|
}
|
|
1340
1427
|
}
|
|
1341
1428
|
const newBarrel = generateUtilsBarrel(resolution.utils, utilExports);
|
|
1342
|
-
const barrelPath =
|
|
1429
|
+
const barrelPath = path8.join(libDir, "rootnative-utils.ts");
|
|
1343
1430
|
let oldBarrel = "";
|
|
1344
|
-
if (await
|
|
1345
|
-
oldBarrel = await
|
|
1431
|
+
if (await fs8.pathExists(barrelPath)) {
|
|
1432
|
+
oldBarrel = await fs8.readFile(barrelPath, "utf-8");
|
|
1346
1433
|
}
|
|
1347
1434
|
utilDiffs.push(computeDiff(oldBarrel, newBarrel, "lib/rootnative-utils.ts"));
|
|
1348
1435
|
const changedComponents = componentDiffs.filter((c) => c.hasChanges);
|
|
@@ -1405,8 +1492,8 @@ async function updateCommand(componentNames, cwd, options) {
|
|
|
1405
1492
|
const applySpinner = createSpinner("Applying updates...");
|
|
1406
1493
|
applySpinner.start();
|
|
1407
1494
|
for (const comp of changedComponents) {
|
|
1408
|
-
const componentDir =
|
|
1409
|
-
await
|
|
1495
|
+
const componentDir = path8.join(componentsDir, comp.name);
|
|
1496
|
+
await fs8.ensureDir(componentDir);
|
|
1410
1497
|
if (comp.diffs.length === 0) {
|
|
1411
1498
|
const entry = resolution.components.find(
|
|
1412
1499
|
(c) => c.entry.name === comp.name
|
|
@@ -1414,14 +1501,14 @@ async function updateCommand(componentNames, cwd, options) {
|
|
|
1414
1501
|
if (!entry) continue;
|
|
1415
1502
|
for (const filePath of entry.entry.files) {
|
|
1416
1503
|
const content = await fetchFileContent(config, filePath);
|
|
1417
|
-
const fileName =
|
|
1504
|
+
const fileName = path8.basename(filePath);
|
|
1418
1505
|
const transformed = transformImports(content, {
|
|
1419
1506
|
config,
|
|
1420
1507
|
componentName: comp.name,
|
|
1421
1508
|
installedComponents: allComponentNames
|
|
1422
1509
|
});
|
|
1423
|
-
await
|
|
1424
|
-
|
|
1510
|
+
await fs8.writeFile(
|
|
1511
|
+
path8.join(componentDir, fileName),
|
|
1425
1512
|
transformed,
|
|
1426
1513
|
"utf-8"
|
|
1427
1514
|
);
|
|
@@ -1431,23 +1518,23 @@ async function updateCommand(componentNames, cwd, options) {
|
|
|
1431
1518
|
for (const diff of comp.diffs) {
|
|
1432
1519
|
if (!diff.hasChanges) continue;
|
|
1433
1520
|
const newContent = diff.lines.filter((l) => l.type !== "remove").map((l) => l.content).join("\n");
|
|
1434
|
-
await
|
|
1435
|
-
|
|
1521
|
+
await fs8.writeFile(
|
|
1522
|
+
path8.join(componentDir, diff.fileName),
|
|
1436
1523
|
newContent,
|
|
1437
1524
|
"utf-8"
|
|
1438
1525
|
);
|
|
1439
1526
|
}
|
|
1440
1527
|
}
|
|
1441
|
-
await
|
|
1528
|
+
await fs8.ensureDir(libDir);
|
|
1442
1529
|
for (const diff of changedUtils) {
|
|
1443
1530
|
if (!diff.hasChanges) continue;
|
|
1444
1531
|
const newContent = diff.lines.filter((l) => l.type !== "remove").map((l) => l.content).join("\n");
|
|
1445
|
-
const filePath =
|
|
1532
|
+
const filePath = path8.join(
|
|
1446
1533
|
libDir,
|
|
1447
1534
|
// diff.fileName is like "lib/color.ts" — strip the "lib/" prefix
|
|
1448
1535
|
diff.fileName.replace(/^lib\//, "")
|
|
1449
1536
|
);
|
|
1450
|
-
await
|
|
1537
|
+
await fs8.writeFile(filePath, newContent, "utf-8");
|
|
1451
1538
|
}
|
|
1452
1539
|
applySpinner.succeed("Updates applied");
|
|
1453
1540
|
logger.break();
|
|
@@ -1457,17 +1544,17 @@ async function updateCommand(componentNames, cwd, options) {
|
|
|
1457
1544
|
logger.break();
|
|
1458
1545
|
}
|
|
1459
1546
|
async function getInstalledComponents(componentsDir) {
|
|
1460
|
-
if (!await
|
|
1547
|
+
if (!await fs8.pathExists(componentsDir)) {
|
|
1461
1548
|
return [];
|
|
1462
1549
|
}
|
|
1463
|
-
const entries = await
|
|
1550
|
+
const entries = await fs8.readdir(componentsDir);
|
|
1464
1551
|
const components = [];
|
|
1465
1552
|
for (const entry of entries) {
|
|
1466
|
-
const fullPath =
|
|
1467
|
-
const stat = await
|
|
1553
|
+
const fullPath = path8.join(componentsDir, entry);
|
|
1554
|
+
const stat = await fs8.stat(fullPath);
|
|
1468
1555
|
if (stat.isDirectory()) {
|
|
1469
|
-
const indexPath =
|
|
1470
|
-
if (await
|
|
1556
|
+
const indexPath = path8.join(fullPath, "index.ts");
|
|
1557
|
+
if (await fs8.pathExists(indexPath)) {
|
|
1471
1558
|
components.push(entry);
|
|
1472
1559
|
}
|
|
1473
1560
|
}
|
|
@@ -1476,10 +1563,10 @@ async function getInstalledComponents(componentsDir) {
|
|
|
1476
1563
|
}
|
|
1477
1564
|
|
|
1478
1565
|
// src/commands/upgrade.ts
|
|
1479
|
-
import
|
|
1566
|
+
import path9 from "path";
|
|
1480
1567
|
import chalk9 from "chalk";
|
|
1481
1568
|
import { execa as execa4 } from "execa";
|
|
1482
|
-
import
|
|
1569
|
+
import fs9 from "fs-extra";
|
|
1483
1570
|
import prompts5 from "prompts";
|
|
1484
1571
|
async function fetchLatestFromNpm(packageName) {
|
|
1485
1572
|
const url = `https://registry.npmjs.org/${packageName}/latest`;
|
|
@@ -1492,16 +1579,16 @@ async function fetchLatestFromNpm(packageName) {
|
|
|
1492
1579
|
return response.json();
|
|
1493
1580
|
}
|
|
1494
1581
|
function getInstalledPackageInfo(cwd, packageName) {
|
|
1495
|
-
const pkgPath =
|
|
1582
|
+
const pkgPath = path9.resolve(
|
|
1496
1583
|
cwd,
|
|
1497
1584
|
"node_modules",
|
|
1498
1585
|
...packageName.split("/"),
|
|
1499
1586
|
"package.json"
|
|
1500
1587
|
);
|
|
1501
|
-
if (!
|
|
1588
|
+
if (!fs9.pathExistsSync(pkgPath)) {
|
|
1502
1589
|
return null;
|
|
1503
1590
|
}
|
|
1504
|
-
const pkg =
|
|
1591
|
+
const pkg = fs9.readJSONSync(pkgPath);
|
|
1505
1592
|
return {
|
|
1506
1593
|
version: pkg.version,
|
|
1507
1594
|
peerDependencies: pkg.peerDependencies,
|
|
@@ -1532,9 +1619,9 @@ function diffPeerDeps(current, latest) {
|
|
|
1532
1619
|
return { added, changed, removed };
|
|
1533
1620
|
}
|
|
1534
1621
|
function getProjectDeps(cwd) {
|
|
1535
|
-
const pkgPath =
|
|
1622
|
+
const pkgPath = path9.resolve(cwd, "package.json");
|
|
1536
1623
|
try {
|
|
1537
|
-
const pkg =
|
|
1624
|
+
const pkg = fs9.readJSONSync(pkgPath);
|
|
1538
1625
|
return {
|
|
1539
1626
|
...pkg.dependencies,
|
|
1540
1627
|
...pkg.devDependencies
|
package/llms.txt
CHANGED
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rootnative/cli",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.2",
|
|
4
4
|
"description": "CLI for adding RootNative UI components to your React Native project",
|
|
5
5
|
"private": false,
|
|
6
6
|
"bin": {
|
|
7
7
|
"rootnative": "dist/index.mjs"
|
|
8
8
|
},
|
|
9
|
-
"exports":
|
|
9
|
+
"exports": {
|
|
10
|
+
"./package.json": "./package.json",
|
|
11
|
+
".": "./dist/index.mjs"
|
|
12
|
+
},
|
|
10
13
|
"files": [
|
|
11
14
|
"dist",
|
|
12
15
|
"llms.txt"
|