@sublime-ui/devkit 0.1.0 → 0.1.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 +1 -1
- package/dist/chunk-PHO3DVF2.js +810 -0
- package/dist/cli.js +201 -123
- package/dist/index.d.ts +31 -1
- package/dist/index.js +6 -0
- package/package.json +60 -43
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
FileExistsError,
|
|
4
|
+
initApp,
|
|
5
|
+
log,
|
|
6
|
+
run,
|
|
7
|
+
runInherit,
|
|
8
|
+
safeWrite
|
|
9
|
+
} from "./chunk-PHO3DVF2.js";
|
|
2
10
|
|
|
3
11
|
// src/cli.ts
|
|
4
12
|
import { Command } from "commander";
|
|
@@ -6,31 +14,6 @@ import { Command } from "commander";
|
|
|
6
14
|
// src/lib/probe.ts
|
|
7
15
|
import { join } from "path";
|
|
8
16
|
|
|
9
|
-
// src/util/exec.ts
|
|
10
|
-
import { execa } from "execa";
|
|
11
|
-
async function run(file, args, opts = {}) {
|
|
12
|
-
const result = await execa(file, args, {
|
|
13
|
-
...opts.cwd === void 0 ? {} : { cwd: opts.cwd },
|
|
14
|
-
env: { ...process.env, ...opts.env },
|
|
15
|
-
reject: false,
|
|
16
|
-
all: false
|
|
17
|
-
});
|
|
18
|
-
return {
|
|
19
|
-
stdout: result.stdout ?? "",
|
|
20
|
-
stderr: result.stderr ?? "",
|
|
21
|
-
exitCode: result.exitCode ?? 1
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
async function runInherit(file, args, opts = {}) {
|
|
25
|
-
const result = await execa(file, args, {
|
|
26
|
-
...opts.cwd === void 0 ? {} : { cwd: opts.cwd },
|
|
27
|
-
env: { ...process.env, ...opts.env },
|
|
28
|
-
stdio: "inherit",
|
|
29
|
-
reject: false
|
|
30
|
-
});
|
|
31
|
-
return result.exitCode ?? 1;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
17
|
// src/lib/detect.ts
|
|
35
18
|
function parseJavaVersion(stderr) {
|
|
36
19
|
const match = stderr.match(/version "([^"]+)"/);
|
|
@@ -155,23 +138,6 @@ function buildDoctorReport(probes) {
|
|
|
155
138
|
return { rows, ok: rows.every((r) => r.ok) };
|
|
156
139
|
}
|
|
157
140
|
|
|
158
|
-
// src/util/log.ts
|
|
159
|
-
import pc from "picocolors";
|
|
160
|
-
var log = {
|
|
161
|
-
info: (m) => console.log(m),
|
|
162
|
-
step: (m) => console.log(pc.cyan(`\u2192 ${m}`)),
|
|
163
|
-
success: (m) => console.log(pc.green(`\u2713 ${m}`)),
|
|
164
|
-
warn: (m) => console.log(pc.yellow(`! ${m}`)),
|
|
165
|
-
error: (m) => console.error(pc.red(`\u2717 ${m}`)),
|
|
166
|
-
table: (rows) => {
|
|
167
|
-
const width = rows.reduce((m, r) => Math.max(m, r.label.length), 0);
|
|
168
|
-
for (const r of rows) {
|
|
169
|
-
const mark = r.ok ? pc.green("\u2713") : pc.red("\u2717");
|
|
170
|
-
console.log(`${mark} ${r.label.padEnd(width)} ${pc.dim(r.detail)}`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
|
|
175
141
|
// src/commands/doctor.ts
|
|
176
142
|
async function doctorCommand() {
|
|
177
143
|
log.step("Checking environment for offline Android builds\u2026");
|
|
@@ -546,10 +512,10 @@ async function runCommand(opts) {
|
|
|
546
512
|
}
|
|
547
513
|
|
|
548
514
|
// src/cli.ts
|
|
549
|
-
import { input } from "@inquirer/prompts";
|
|
515
|
+
import { input as input2 } from "@inquirer/prompts";
|
|
550
516
|
|
|
551
517
|
// src/commands/make-model.ts
|
|
552
|
-
import { existsSync as
|
|
518
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
553
519
|
import { join as join7 } from "path";
|
|
554
520
|
|
|
555
521
|
// src/lib/generators/config.ts
|
|
@@ -581,8 +547,8 @@ function loadConfig(cwd) {
|
|
|
581
547
|
}
|
|
582
548
|
|
|
583
549
|
// src/lib/generators/names.ts
|
|
584
|
-
function pascalCase(
|
|
585
|
-
return
|
|
550
|
+
function pascalCase(input3) {
|
|
551
|
+
return input3.split(/[^A-Za-z0-9]+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
586
552
|
}
|
|
587
553
|
function pluralize(word) {
|
|
588
554
|
const w = word.toLowerCase();
|
|
@@ -603,10 +569,10 @@ function deriveNames(name) {
|
|
|
603
569
|
|
|
604
570
|
// src/lib/generators/fields.ts
|
|
605
571
|
var SCALARS = /* @__PURE__ */ new Set(["string", "number", "boolean"]);
|
|
606
|
-
function parseFields(
|
|
572
|
+
function parseFields(input3) {
|
|
607
573
|
const fields = [];
|
|
608
574
|
const warnings = [];
|
|
609
|
-
for (const part of
|
|
575
|
+
for (const part of input3.split(",")) {
|
|
610
576
|
const trimmed = part.trim();
|
|
611
577
|
if (trimmed === "") continue;
|
|
612
578
|
const [rawName, rawType] = trimmed.split(":").map((s) => s.trim());
|
|
@@ -647,24 +613,6 @@ function updateBarrel(existing, line) {
|
|
|
647
613
|
`;
|
|
648
614
|
}
|
|
649
615
|
|
|
650
|
-
// src/lib/generators/write.ts
|
|
651
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
652
|
-
import { dirname } from "path";
|
|
653
|
-
var FileExistsError = class _FileExistsError extends Error {
|
|
654
|
-
path;
|
|
655
|
-
constructor(path) {
|
|
656
|
-
super(`File already exists: ${path} (use --force to overwrite)`);
|
|
657
|
-
this.name = "FileExistsError";
|
|
658
|
-
this.path = path;
|
|
659
|
-
Object.setPrototypeOf(this, _FileExistsError.prototype);
|
|
660
|
-
}
|
|
661
|
-
};
|
|
662
|
-
function safeWrite(path, content, force) {
|
|
663
|
-
if (existsSync5(path) && !force) throw new FileExistsError(path);
|
|
664
|
-
mkdirSync2(dirname(path), { recursive: true });
|
|
665
|
-
writeFileSync2(path, content);
|
|
666
|
-
}
|
|
667
|
-
|
|
668
616
|
// src/commands/make-model.ts
|
|
669
617
|
async function makeModel(opts) {
|
|
670
618
|
const cfg = loadConfig(opts.cwd);
|
|
@@ -682,7 +630,7 @@ async function makeModel(opts) {
|
|
|
682
630
|
const barrelPath = join7(opts.cwd, cfg.modelsDir, "index.ts");
|
|
683
631
|
try {
|
|
684
632
|
safeWrite(modelPath, content, opts.force);
|
|
685
|
-
const existing =
|
|
633
|
+
const existing = existsSync5(barrelPath) ? readFileSync3(barrelPath, "utf8") : "";
|
|
686
634
|
safeWrite(barrelPath, updateBarrel(existing, `export * from './${names.fileName}.js';`), true);
|
|
687
635
|
log.success(`Created ${modelPath}`);
|
|
688
636
|
return 0;
|
|
@@ -696,7 +644,7 @@ async function makeModel(opts) {
|
|
|
696
644
|
}
|
|
697
645
|
|
|
698
646
|
// src/commands/make-component.ts
|
|
699
|
-
import { existsSync as
|
|
647
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
|
|
700
648
|
import { join as join8 } from "path";
|
|
701
649
|
|
|
702
650
|
// src/lib/generators/render-component.ts
|
|
@@ -774,7 +722,7 @@ async function makeComponent(opts) {
|
|
|
774
722
|
safeWrite(join8(dir, `${opts.name}.native.tsx`), renderComponentNative(opts.name, cfg.importAlias), opts.force);
|
|
775
723
|
safeWrite(join8(dir, "index.ts"), renderComponentIndex(opts.name), opts.force);
|
|
776
724
|
const barrelPath = join8(opts.cwd, cfg.componentsDir, "index.ts");
|
|
777
|
-
const existing =
|
|
725
|
+
const existing = existsSync6(barrelPath) ? readFileSync4(barrelPath, "utf8") : "";
|
|
778
726
|
safeWrite(barrelPath, updateBarrel(existing, `export * from './${opts.name}/index.js';`), true);
|
|
779
727
|
log.success(`Created ${dir}`);
|
|
780
728
|
return 0;
|
|
@@ -790,7 +738,7 @@ async function makeComponent(opts) {
|
|
|
790
738
|
// src/commands/theme-init.ts
|
|
791
739
|
import { findPackageJSON } from "module";
|
|
792
740
|
import { pathToFileURL } from "url";
|
|
793
|
-
import { dirname
|
|
741
|
+
import { dirname, join as join9 } from "path";
|
|
794
742
|
|
|
795
743
|
// src/lib/generators/render-tokens.ts
|
|
796
744
|
function renderTokensWrapper(importAlias) {
|
|
@@ -807,7 +755,7 @@ async function resolveDefaultTokens(cwd) {
|
|
|
807
755
|
const base = pathToFileURL(join9(cwd, "noop.js")).href;
|
|
808
756
|
const pkgJsonPath = findPackageJSON("@sublime-ui/library", base);
|
|
809
757
|
if (!pkgJsonPath) throw new Error("@sublime-ui/library not found");
|
|
810
|
-
const tokensJs = join9(
|
|
758
|
+
const tokensJs = join9(dirname(pkgJsonPath), "dist", "tokens", "tokens.js");
|
|
811
759
|
const mod = await import(pathToFileURL(tokensJs).href);
|
|
812
760
|
return mod.defaultTokens;
|
|
813
761
|
}
|
|
@@ -838,54 +786,137 @@ async function themeInit(opts) {
|
|
|
838
786
|
|
|
839
787
|
// src/commands/build-nav.ts
|
|
840
788
|
import { join as join10 } from "path";
|
|
841
|
-
import { readFileSync as
|
|
842
|
-
|
|
843
|
-
// src/lib/navigation/
|
|
844
|
-
import {
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
789
|
+
import { readFileSync as readFileSync6, watch as fsWatch } from "fs";
|
|
790
|
+
|
|
791
|
+
// src/lib/navigation/analyze-storybook.ts
|
|
792
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
793
|
+
import ts from "typescript";
|
|
794
|
+
function analyzeStorybook(absFile) {
|
|
795
|
+
const source = readFileSync5(absFile, "utf8");
|
|
796
|
+
const sf = ts.createSourceFile(absFile, source, ts.ScriptTarget.Latest, true);
|
|
797
|
+
const localBooks = collectLocalBooks(sf);
|
|
798
|
+
const rootCall = findDefaultBookCall(sf, localBooks);
|
|
799
|
+
if (rootCall === void 0) {
|
|
800
|
+
throw new Error(`Storybook ${absFile} must default-export a book().`);
|
|
849
801
|
}
|
|
850
|
-
return "
|
|
851
|
-
}
|
|
852
|
-
function isBookDef(value) {
|
|
853
|
-
return value != null && typeof value === "object" && value.kind === "book" && typeof value.pages === "object";
|
|
802
|
+
return bookToNode(rootCall, "root", {}, sf, localBooks);
|
|
854
803
|
}
|
|
855
|
-
function bookToNode(
|
|
804
|
+
function bookToNode(call, key, options, sf, localBooks) {
|
|
805
|
+
const arg = call.arguments[0];
|
|
806
|
+
const config = arg !== void 0 && ts.isObjectLiteralExpression(arg) ? arg : void 0;
|
|
807
|
+
const format = config ? stringProp(config, "format") : void 0;
|
|
808
|
+
const pages = config ? objectProp(config, "pages") : void 0;
|
|
856
809
|
const children = [];
|
|
857
|
-
|
|
858
|
-
|
|
810
|
+
if (pages) {
|
|
811
|
+
for (const prop of pages.properties) {
|
|
812
|
+
if (!ts.isPropertyAssignment(prop)) continue;
|
|
813
|
+
const childKey = propertyName(prop.name);
|
|
814
|
+
if (childKey === void 0) continue;
|
|
815
|
+
if (!ts.isCallExpression(prop.initializer)) continue;
|
|
816
|
+
children.push(entryToNode(prop.initializer, childKey, sf, localBooks));
|
|
817
|
+
}
|
|
859
818
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
819
|
+
const node = { key, kind: "book", options: { ...options }, children };
|
|
820
|
+
if (format !== void 0) node.format = format;
|
|
821
|
+
return node;
|
|
822
|
+
}
|
|
823
|
+
function entryToNode(call, key, sf, localBooks) {
|
|
824
|
+
const callee = ts.isIdentifier(call.expression) ? call.expression.text : void 0;
|
|
825
|
+
if (callee === "link") {
|
|
826
|
+
const options2 = optionsFromCall(call, 1);
|
|
827
|
+
const target = call.arguments[0];
|
|
828
|
+
const targetName = target !== void 0 && ts.isIdentifier(target) ? target.text : void 0;
|
|
829
|
+
const targetBook = targetName !== void 0 ? localBooks.get(targetName) : void 0;
|
|
830
|
+
if (targetBook === void 0) {
|
|
865
831
|
return {
|
|
866
832
|
key,
|
|
867
833
|
kind: "book",
|
|
868
|
-
options:
|
|
834
|
+
options: options2,
|
|
869
835
|
children: [],
|
|
870
836
|
linkError: `link("${key}") does not reference a book().`
|
|
871
837
|
};
|
|
872
838
|
}
|
|
873
|
-
return bookToNode(
|
|
839
|
+
return bookToNode(targetBook, key, options2, sf, localBooks);
|
|
874
840
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
841
|
+
const options = optionsFromCall(call, 1);
|
|
842
|
+
const component = call.arguments[0];
|
|
843
|
+
const componentName = component !== void 0 && ts.isIdentifier(component) ? component.text : void 0;
|
|
844
|
+
const node = { key, kind: "page", options };
|
|
845
|
+
if (componentName !== void 0) node.component = componentName;
|
|
846
|
+
return node;
|
|
847
|
+
}
|
|
848
|
+
function optionsFromCall(call, argIndex) {
|
|
849
|
+
const arg = call.arguments[argIndex];
|
|
850
|
+
if (arg === void 0 || !ts.isObjectLiteralExpression(arg)) return {};
|
|
851
|
+
const options = {};
|
|
852
|
+
const title = stringProp(arg, "title");
|
|
853
|
+
if (title !== void 0) options.title = title;
|
|
854
|
+
const icon = stringProp(arg, "icon");
|
|
855
|
+
if (icon !== void 0) options.icon = icon;
|
|
856
|
+
const path = stringProp(arg, "path");
|
|
857
|
+
if (path !== void 0) options.path = path;
|
|
858
|
+
const initial = booleanProp(arg, "initial");
|
|
859
|
+
if (initial !== void 0) options.initial = initial;
|
|
860
|
+
return options;
|
|
861
|
+
}
|
|
862
|
+
function findDefaultBookCall(sf, localBooks) {
|
|
863
|
+
let found;
|
|
864
|
+
for (const stmt of sf.statements) {
|
|
865
|
+
if (ts.isExportAssignment(stmt) && !stmt.isExportEquals) {
|
|
866
|
+
const expr = stmt.expression;
|
|
867
|
+
if (ts.isCallExpression(expr) && isBookCall(expr)) {
|
|
868
|
+
found = expr;
|
|
869
|
+
} else if (ts.isIdentifier(expr)) {
|
|
870
|
+
const local = localBooks.get(expr.text);
|
|
871
|
+
if (local) found = local;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
return found;
|
|
876
|
+
}
|
|
877
|
+
function isBookCall(call) {
|
|
878
|
+
return ts.isIdentifier(call.expression) && call.expression.text === "book";
|
|
879
|
+
}
|
|
880
|
+
function collectLocalBooks(sf) {
|
|
881
|
+
const books = /* @__PURE__ */ new Map();
|
|
882
|
+
const visit = (node) => {
|
|
883
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer !== void 0 && ts.isCallExpression(node.initializer) && isBookCall(node.initializer)) {
|
|
884
|
+
books.set(node.name.text, node.initializer);
|
|
885
|
+
}
|
|
886
|
+
ts.forEachChild(node, visit);
|
|
880
887
|
};
|
|
888
|
+
visit(sf);
|
|
889
|
+
return books;
|
|
881
890
|
}
|
|
882
|
-
|
|
883
|
-
const
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
891
|
+
function stringProp(obj, name) {
|
|
892
|
+
const init = propInitializer(obj, name);
|
|
893
|
+
if (init && ts.isStringLiteralLike(init)) return init.text;
|
|
894
|
+
return void 0;
|
|
895
|
+
}
|
|
896
|
+
function booleanProp(obj, name) {
|
|
897
|
+
const init = propInitializer(obj, name);
|
|
898
|
+
if (init?.kind === ts.SyntaxKind.TrueKeyword) return true;
|
|
899
|
+
if (init?.kind === ts.SyntaxKind.FalseKeyword) return false;
|
|
900
|
+
return void 0;
|
|
901
|
+
}
|
|
902
|
+
function objectProp(obj, name) {
|
|
903
|
+
const init = propInitializer(obj, name);
|
|
904
|
+
if (init && ts.isObjectLiteralExpression(init)) return init;
|
|
905
|
+
return void 0;
|
|
906
|
+
}
|
|
907
|
+
function propInitializer(obj, name) {
|
|
908
|
+
for (const prop of obj.properties) {
|
|
909
|
+
if (ts.isPropertyAssignment(prop) && propertyName(prop.name) === name) {
|
|
910
|
+
return prop.initializer;
|
|
911
|
+
}
|
|
887
912
|
}
|
|
888
|
-
return
|
|
913
|
+
return void 0;
|
|
914
|
+
}
|
|
915
|
+
function propertyName(name) {
|
|
916
|
+
if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
|
|
917
|
+
return name.text;
|
|
918
|
+
}
|
|
919
|
+
return void 0;
|
|
889
920
|
}
|
|
890
921
|
|
|
891
922
|
// src/lib/navigation/validate.ts
|
|
@@ -981,35 +1012,35 @@ function flatten(root) {
|
|
|
981
1012
|
}
|
|
982
1013
|
|
|
983
1014
|
// src/lib/navigation/extract-params.ts
|
|
984
|
-
import
|
|
1015
|
+
import ts2 from "typescript";
|
|
985
1016
|
function extractParams(source) {
|
|
986
|
-
const sf =
|
|
1017
|
+
const sf = ts2.createSourceFile("storybook.ts", source, ts2.ScriptTarget.Latest, true);
|
|
987
1018
|
const params = /* @__PURE__ */ new Map();
|
|
988
1019
|
const visit = (node) => {
|
|
989
|
-
if (
|
|
990
|
-
const key =
|
|
1020
|
+
if (ts2.isPropertyAssignment(node) && ts2.isCallExpression(node.initializer) && isPageCall(node.initializer) && node.initializer.typeArguments && node.initializer.typeArguments.length > 0) {
|
|
1021
|
+
const key = propertyName2(node.name);
|
|
991
1022
|
if (key !== void 0) {
|
|
992
1023
|
params.set(key, node.initializer.typeArguments[0].getText(sf).trim());
|
|
993
1024
|
}
|
|
994
1025
|
}
|
|
995
|
-
|
|
1026
|
+
ts2.forEachChild(node, visit);
|
|
996
1027
|
};
|
|
997
1028
|
visit(sf);
|
|
998
1029
|
return params;
|
|
999
1030
|
}
|
|
1000
1031
|
function isPageCall(call) {
|
|
1001
|
-
return
|
|
1032
|
+
return ts2.isIdentifier(call.expression) && call.expression.text === "page";
|
|
1002
1033
|
}
|
|
1003
|
-
function
|
|
1004
|
-
if (
|
|
1034
|
+
function propertyName2(name) {
|
|
1035
|
+
if (ts2.isIdentifier(name) || ts2.isStringLiteral(name) || ts2.isNumericLiteral(name)) {
|
|
1005
1036
|
return name.text;
|
|
1006
1037
|
}
|
|
1007
1038
|
return void 0;
|
|
1008
1039
|
}
|
|
1009
1040
|
|
|
1010
1041
|
// src/lib/navigation/merge-route-params.ts
|
|
1011
|
-
function mergeRouteParams(
|
|
1012
|
-
const { nativeKeys, webKeys, nativeParams, webParams } =
|
|
1042
|
+
function mergeRouteParams(input3) {
|
|
1043
|
+
const { nativeKeys, webKeys, nativeParams, webParams } = input3;
|
|
1013
1044
|
const keys = [.../* @__PURE__ */ new Set([...nativeKeys, ...webKeys])].sort();
|
|
1014
1045
|
const routes = [];
|
|
1015
1046
|
const conflicts = [];
|
|
@@ -1062,7 +1093,7 @@ function renderNative(root, opts) {
|
|
|
1062
1093
|
const renderBook = (book) => {
|
|
1063
1094
|
const { factory, nav } = factoryFor(book.format);
|
|
1064
1095
|
const navVar = navVarFor(factory, nav);
|
|
1065
|
-
const
|
|
1096
|
+
const componentName = book.key === "root" ? "RootNavigator" : `${pascal(book.key)}Navigator`;
|
|
1066
1097
|
const supportsIcon = factory === "createBottomTabNavigator" || factory === "createDrawerNavigator";
|
|
1067
1098
|
const children = book.children ?? [];
|
|
1068
1099
|
const screens = [];
|
|
@@ -1079,7 +1110,7 @@ function renderNative(root, opts) {
|
|
|
1079
1110
|
const initialChild = children.find((c) => c.options.initial === true);
|
|
1080
1111
|
const navProps = initialChild ? ` initialRouteName="${initialChild.key}"` : "";
|
|
1081
1112
|
navigatorBlocks.push(
|
|
1082
|
-
`function ${
|
|
1113
|
+
`function ${componentName}() {
|
|
1083
1114
|
return (
|
|
1084
1115
|
<${navVar}.Navigator${navProps}>
|
|
1085
1116
|
${screens.join("\n")}
|
|
@@ -1087,7 +1118,7 @@ ${screens.join("\n")}
|
|
|
1087
1118
|
);
|
|
1088
1119
|
}`
|
|
1089
1120
|
);
|
|
1090
|
-
return
|
|
1121
|
+
return componentName;
|
|
1091
1122
|
};
|
|
1092
1123
|
const rootComponent = renderBook(root);
|
|
1093
1124
|
const factoryImports = [...usedFactories.entries()].map(([factory, nav]) => {
|
|
@@ -1298,8 +1329,8 @@ async function buildOnce(navDir, nativeFile, webFile) {
|
|
|
1298
1329
|
let nativeRoot;
|
|
1299
1330
|
let webRoot;
|
|
1300
1331
|
try {
|
|
1301
|
-
nativeRoot =
|
|
1302
|
-
webRoot =
|
|
1332
|
+
nativeRoot = analyzeStorybook(nativeFile);
|
|
1333
|
+
webRoot = analyzeStorybook(webFile);
|
|
1303
1334
|
} catch (err) {
|
|
1304
1335
|
log.error(err instanceof Error ? err.message : String(err));
|
|
1305
1336
|
return 1;
|
|
@@ -1313,8 +1344,8 @@ async function buildOnce(navDir, nativeFile, webFile) {
|
|
|
1313
1344
|
log.error(`build:nav failed with ${diagnostics.length} error(s); wrote nothing.`);
|
|
1314
1345
|
return 1;
|
|
1315
1346
|
}
|
|
1316
|
-
const nativeParams = extractParams(
|
|
1317
|
-
const webParams = extractParams(
|
|
1347
|
+
const nativeParams = extractParams(readFileSync6(nativeFile, "utf8"));
|
|
1348
|
+
const webParams = extractParams(readFileSync6(webFile, "utf8"));
|
|
1318
1349
|
const { routes, conflicts } = mergeRouteParams({
|
|
1319
1350
|
nativeKeys: flatten(nativeRoot).routes.map((r) => r.key),
|
|
1320
1351
|
webKeys: flatten(webRoot).routes.map((r) => r.key),
|
|
@@ -1334,7 +1365,7 @@ async function buildOnce(navDir, nativeFile, webFile) {
|
|
|
1334
1365
|
const webSrc = renderWeb(webRoot, { screensImport: SCREENS_IMPORT });
|
|
1335
1366
|
const dtsSrc = renderRoutesDts(routes);
|
|
1336
1367
|
safeWrite(join10(navDir, "navigation.native.tsx"), nativeSrc, true);
|
|
1337
|
-
safeWrite(join10(navDir, "navigation.
|
|
1368
|
+
safeWrite(join10(navDir, "navigation.tsx"), webSrc, true);
|
|
1338
1369
|
safeWrite(join10(navDir, "routes.d.ts"), dtsSrc, true);
|
|
1339
1370
|
safeWrite(join10(navDir, "index.ts"), BARREL, true);
|
|
1340
1371
|
log.success(`build:nav wrote 4 files to ${navDir}`);
|
|
@@ -1372,6 +1403,42 @@ async function desktopBuild(opts) {
|
|
|
1372
1403
|
return runner("electron-forge", ["make"], desktopDir);
|
|
1373
1404
|
}
|
|
1374
1405
|
|
|
1406
|
+
// src/commands/init.ts
|
|
1407
|
+
import { input, checkbox } from "@inquirer/prompts";
|
|
1408
|
+
import { basename } from "path";
|
|
1409
|
+
var ALL = ["web", "mobile", "desktop"];
|
|
1410
|
+
function parseTargets(spec) {
|
|
1411
|
+
if (!spec) return void 0;
|
|
1412
|
+
const parts = spec.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1413
|
+
return parts.filter((p) => ALL.includes(p));
|
|
1414
|
+
}
|
|
1415
|
+
var inquirerPrompt = async ({ dir }) => {
|
|
1416
|
+
const name = await input({ message: "App name:", default: basename(dir) });
|
|
1417
|
+
const targets = await checkbox({
|
|
1418
|
+
message: "Targets:",
|
|
1419
|
+
choices: [
|
|
1420
|
+
{ name: "web (Vite + MUI)", value: "web", checked: true },
|
|
1421
|
+
{ name: "mobile (React Native + Paper)", value: "mobile", checked: true },
|
|
1422
|
+
{ name: "desktop (Electron, wraps web)", value: "desktop", checked: true }
|
|
1423
|
+
]
|
|
1424
|
+
});
|
|
1425
|
+
return { name, targets };
|
|
1426
|
+
};
|
|
1427
|
+
async function runInit(opts) {
|
|
1428
|
+
const targets = parseTargets(opts.targets);
|
|
1429
|
+
return initApp({
|
|
1430
|
+
dir: opts.dir,
|
|
1431
|
+
...opts.name !== void 0 ? { name: opts.name } : {},
|
|
1432
|
+
...targets !== void 0 ? { targets } : {},
|
|
1433
|
+
install: opts.install,
|
|
1434
|
+
git: opts.git,
|
|
1435
|
+
force: opts.force,
|
|
1436
|
+
yes: opts.yes,
|
|
1437
|
+
prompt: opts.prompt ?? inquirerPrompt,
|
|
1438
|
+
...opts.runner !== void 0 ? { runner: opts.runner } : {}
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1375
1442
|
// src/cli.ts
|
|
1376
1443
|
var program = new Command();
|
|
1377
1444
|
program.name("sublime").description("Sublime UI devkit \u2014 offline Android builds and tooling").version("0.0.0");
|
|
@@ -1381,6 +1448,17 @@ program.command("doctor").description("Check the environment for offline Android
|
|
|
1381
1448
|
program.command("setup").description("Install/repair the build environment").action(async () => {
|
|
1382
1449
|
process.exit(await setupCommand());
|
|
1383
1450
|
});
|
|
1451
|
+
program.command("init [dir]").description("Scaffold a new Sublime app (web/mobile/desktop)").option("--name <name>", "app (npm package) name").option("--targets <list>", "comma-separated: web,mobile,desktop").option("--no-install", "skip npm install").option("--no-git", "skip git init").option("--force", "scaffold into a non-empty directory").option("-y, --yes", "accept defaults, no prompts").action(async (dir, opts) => {
|
|
1452
|
+
process.exit(await runInit({
|
|
1453
|
+
dir: dir ?? process.cwd(),
|
|
1454
|
+
...opts.name !== void 0 ? { name: opts.name } : {},
|
|
1455
|
+
...opts.targets !== void 0 ? { targets: opts.targets } : {},
|
|
1456
|
+
install: opts.install,
|
|
1457
|
+
git: opts.git,
|
|
1458
|
+
force: opts.force ?? false,
|
|
1459
|
+
yes: opts.yes ?? false
|
|
1460
|
+
}));
|
|
1461
|
+
});
|
|
1384
1462
|
program.command("build").description("Build a standalone Android APK/AAB offline").option("--release", "release APK (default)", true).option("--debug", "debug APK (requires Metro)").option("--aab", "Android App Bundle (bundleRelease)").option("--project <path>", "project directory", process.cwd()).action(async (opts) => {
|
|
1385
1463
|
const code = await buildCommand({
|
|
1386
1464
|
project: opts.project,
|
|
@@ -1403,7 +1481,7 @@ program.command("make:model <name>").description("Scaffold a Model (+ registerMo
|
|
|
1403
1481
|
force: opts.force ?? false,
|
|
1404
1482
|
...opts.fields ? { fields: opts.fields } : {},
|
|
1405
1483
|
...opts.resource ? { resource: opts.resource } : {},
|
|
1406
|
-
promptFields: () =>
|
|
1484
|
+
promptFields: () => input2({ message: "Fields (name:type, comma-separated; blank for id-only):", default: "" })
|
|
1407
1485
|
});
|
|
1408
1486
|
process.exit(code);
|
|
1409
1487
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
type Target = 'web' | 'mobile' | 'desktop';
|
|
2
|
+
interface ScaffoldFile {
|
|
3
|
+
/** Path relative to the app root, POSIX-style. */
|
|
4
|
+
path: string;
|
|
5
|
+
contents: string;
|
|
6
|
+
}
|
|
7
|
+
interface ScaffoldOptions {
|
|
8
|
+
dir: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
targets?: Target[];
|
|
11
|
+
force?: boolean;
|
|
12
|
+
install?: boolean;
|
|
13
|
+
git?: boolean;
|
|
14
|
+
yes?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type Prompt = (ctx: {
|
|
18
|
+
dir: string;
|
|
19
|
+
}) => Promise<{
|
|
20
|
+
name: string;
|
|
21
|
+
targets: Target[];
|
|
22
|
+
}>;
|
|
23
|
+
type PostRunner = (cmd: string, args: string[], cwd: string) => Promise<number>;
|
|
24
|
+
/** npm package-name rules: lowercase, url-safe, no leading dot/underscore, <=214 chars. */
|
|
25
|
+
declare function isValidNpmName(s: string): boolean;
|
|
26
|
+
declare function initApp(opts: ScaffoldOptions & {
|
|
27
|
+
prompt?: Prompt;
|
|
28
|
+
runner?: PostRunner;
|
|
29
|
+
}): Promise<number>;
|
|
30
|
+
|
|
1
31
|
declare const version = "0.0.0";
|
|
2
32
|
|
|
3
|
-
export { version };
|
|
33
|
+
export { type PostRunner, type Prompt, type ScaffoldFile, type ScaffoldOptions, type Target, initApp, isValidNpmName, version };
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,43 +1,60 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@sublime-ui/devkit",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "The Sublime UI CLI (sublime) — offline Android builds, code generators, the navigation compiler, and desktop tooling.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
},
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
},
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@sublime-ui/devkit",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "The Sublime UI CLI (sublime) — offline Android builds, code generators, the navigation compiler, and desktop tooling.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"sublime-ui",
|
|
7
|
+
"cli",
|
|
8
|
+
"devkit",
|
|
9
|
+
"codegen",
|
|
10
|
+
"scaffolding",
|
|
11
|
+
"electron-forge",
|
|
12
|
+
"android",
|
|
13
|
+
"react-native",
|
|
14
|
+
"typescript"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://sublime-ui.github.io/sublime-ui/",
|
|
17
|
+
"bugs": "https://github.com/sublime-ui/sublime-ui/issues",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/sublime-ui/sublime-ui.git",
|
|
21
|
+
"directory": "devkit"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "Aaron Mkandawire",
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"type": "module",
|
|
29
|
+
"bin": {
|
|
30
|
+
"sublime": "./dist/cli.js",
|
|
31
|
+
"sui": "./dist/cli.js"
|
|
32
|
+
},
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"import": "./dist/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsup",
|
|
44
|
+
"typecheck": "tsc --noEmit && npm run typecheck:fixture",
|
|
45
|
+
"typecheck:fixture": "tsc -p test/fixtures/nav-app/tsconfig.json",
|
|
46
|
+
"test": "vitest run --exclude \"**/node_modules/**\" --exclude \"**/dist/**\" --exclude \"**/test/e2e/**\"",
|
|
47
|
+
"test:e2e": "vitest run test/e2e",
|
|
48
|
+
"lint": "eslint src"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@inquirer/prompts": "^7.0.0",
|
|
52
|
+
"commander": "^12.1.0",
|
|
53
|
+
"execa": "^9.4.0",
|
|
54
|
+
"picocolors": "^1.1.0",
|
|
55
|
+
"typescript": "^5.6.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@types/node": "^22.19.21"
|
|
59
|
+
}
|
|
60
|
+
}
|