create-jilatax 0.0.3 โ 0.0.4
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 +38 -24
- package/THIRD_PARTY_NOTICES.md +7 -0
- package/dist/bin.cjs +6 -0
- package/dist/bin.d.cts +1 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +8 -0
- package/dist/cli-BO6toXiq.cjs +450 -0
- package/dist/cli-BQK3vkvN.js +385 -0
- package/dist/index.cjs +8 -6
- package/dist/index.d.cts +32 -3
- package/dist/index.d.ts +32 -3
- package/dist/index.js +2 -6
- package/package.json +13 -5
- package/template/README.md.tmpl +31 -0
- package/template/android/app/build.gradle.kts +112 -0
- package/template/android/app/proguard-rules.pro +1 -0
- package/template/android/app/src/debug/AndroidManifest.xml +8 -0
- package/template/android/app/src/main/AndroidManifest.xml +30 -0
- package/template/android/app/src/main/res/drawable/jilatax_mark_foreground.xml +11 -0
- package/template/android/app/src/main/res/drawable/jilatax_splash.xml +9 -0
- package/template/android/app/src/main/res/mipmap-anydpi/jilatax_launcher.xml +12 -0
- package/template/android/app/src/main/res/mipmap-anydpi/jilatax_launcher_round.xml +12 -0
- package/template/android/app/src/main/res/mipmap-anydpi-v26/jilatax_launcher.xml +5 -0
- package/template/android/app/src/main/res/mipmap-anydpi-v26/jilatax_launcher_round.xml +5 -0
- package/template/android/app/src/main/res/values/styles.xml +16 -0
- package/template/android/app/src/main/res/values-v31/styles.xml +8 -0
- package/template/android/build.gradle.kts +5 -0
- package/template/android/gradle/wrapper/gradle-wrapper.jar.base64 +1112 -0
- package/template/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/template/android/gradle.properties +6 -0
- package/template/android/gradlew +248 -0
- package/template/android/gradlew.bat +92 -0
- package/template/android/keystore.properties.example +5 -0
- package/template/android/settings.gradle.kts.tmpl +29 -0
- package/template/gitignore +22 -0
- package/template/lynx.config.ts +17 -0
- package/template/src/App.css +95 -0
- package/template/src/App.tsx.tmpl +23 -0
- package/template/src/index.tsx +9 -0
- package/template/src/rspeedy-env.d.ts +1 -0
- package/template/src/tsconfig.json +13 -0
- package/template/tsconfig.json +15 -0
- package/template/tsconfig.node.json +12 -0
package/README.md
CHANGED
|
@@ -1,21 +1,50 @@
|
|
|
1
1
|
# create-jilatax
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Create an Android-first Lynx application backed by the Jilatax runtime and CLI.
|
|
4
4
|
|
|
5
|
-
Requires Node.js 22.18 or newer.
|
|
5
|
+
Requires Node.js 22.18 or newer and Bun.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Create an application
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
10
|
+
bunx create-jilatax@latest my-app
|
|
11
|
+
cd my-app
|
|
12
|
+
bun run run:android
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
The creator asks for the application name, Android package ID, and whether to
|
|
16
|
+
install dependencies. In non-interactive environments, pass the directory and
|
|
17
|
+
options explicitly:
|
|
14
18
|
|
|
15
|
-
```
|
|
16
|
-
|
|
19
|
+
```bash
|
|
20
|
+
bunx create-jilatax@latest my-app \
|
|
21
|
+
--name "My App" \
|
|
22
|
+
--package-id com.example.my_app \
|
|
23
|
+
--skip-install
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Generated projects include:
|
|
27
|
+
|
|
28
|
+
- a Lynx and Rspeedy application;
|
|
29
|
+
- `app.json` as the Jilatax application configuration;
|
|
30
|
+
- an Android Gradle project linked to `jilatax/android`;
|
|
31
|
+
- `bun run run:android` for development on a connected device;
|
|
32
|
+
- `bun run create:aab` for a Play Store Android App Bundle.
|
|
33
|
+
|
|
34
|
+
The creator only writes the project and can run `bun install`. Device, Gradle,
|
|
35
|
+
bundle, APK, and AAB orchestration belongs to `@jilatax/cli`.
|
|
17
36
|
|
|
18
|
-
|
|
37
|
+
## Programmatic API
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { createProject } from 'create-jilatax';
|
|
41
|
+
|
|
42
|
+
await createProject({
|
|
43
|
+
targetDirectory: './my-app',
|
|
44
|
+
displayName: 'My App',
|
|
45
|
+
packageId: 'com.example.my_app',
|
|
46
|
+
install: false,
|
|
47
|
+
});
|
|
19
48
|
```
|
|
20
49
|
|
|
21
50
|
## Development
|
|
@@ -25,21 +54,6 @@ bun install
|
|
|
25
54
|
bun run check
|
|
26
55
|
```
|
|
27
56
|
|
|
28
|
-
##
|
|
57
|
+
## License
|
|
29
58
|
|
|
30
59
|
MIT ยฉ [JilataX](https://jilatax.dev) โ see [`LICENSE`](./LICENSE).
|
|
31
|
-
|
|
32
|
-
<br>
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
<div align="center">
|
|
37
|
-
<p>
|
|
38
|
-
Built with <strong>Bun</strong>, <strong>TypeScript</strong>, and <strong>Lynx</strong>.
|
|
39
|
-
</p>
|
|
40
|
-
<p>
|
|
41
|
-
<a href="https://www.jilatax.dev/jilatax">Website</a> ยท
|
|
42
|
-
<a href="https://github.com/jilatax/jilatax">GitHub</a> ยท
|
|
43
|
-
<a href="https://www.npmjs.com/package/jilatax">npm</a>
|
|
44
|
-
</p>
|
|
45
|
-
</div>
|
package/dist/bin.cjs
ADDED
package/dist/bin.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
//#endregion
|
|
23
|
+
let _clack_prompts = require("@clack/prompts");
|
|
24
|
+
_clack_prompts = __toESM(_clack_prompts, 1);
|
|
25
|
+
let node_fs_promises = require("node:fs/promises");
|
|
26
|
+
let node_process = require("node:process");
|
|
27
|
+
let node_child_process = require("node:child_process");
|
|
28
|
+
let node_path = require("node:path");
|
|
29
|
+
node_path = __toESM(node_path, 1);
|
|
30
|
+
let node_url = require("node:url");
|
|
31
|
+
let jilatax = require("jilatax");
|
|
32
|
+
//#region src/generator.ts
|
|
33
|
+
const JILATAX_CLI_VERSION = "^0.0.7";
|
|
34
|
+
const JILATAX_VERSION = "^0.0.5";
|
|
35
|
+
const LYNX_REACT_VERSION = "^0.116.2";
|
|
36
|
+
const LYNX_TYPES_VERSION = "^3.7.0";
|
|
37
|
+
const REACT_PLUGIN_VERSION = "^0.12.7";
|
|
38
|
+
const RSPEEDY_VERSION = "^0.13.3";
|
|
39
|
+
const TEXT_TEMPLATE_FILES = /* @__PURE__ */ new Set([
|
|
40
|
+
"README.md.tmpl",
|
|
41
|
+
"android/settings.gradle.kts.tmpl",
|
|
42
|
+
"src/App.tsx.tmpl"
|
|
43
|
+
]);
|
|
44
|
+
async function createProject(options) {
|
|
45
|
+
const projectDirectory = node_path.default.resolve(options.targetDirectory);
|
|
46
|
+
const projectName = normalizeProjectName(node_path.default.basename(projectDirectory));
|
|
47
|
+
const displayName = normalizeDisplayName(options.displayName ?? titleCase$1(projectName));
|
|
48
|
+
const packageId = validatePackageId(options.packageId ?? defaultPackageId(projectName));
|
|
49
|
+
const config = createInitialConfig(displayName, projectName, packageId);
|
|
50
|
+
await assertTargetDoesNotExist(projectDirectory);
|
|
51
|
+
const parentDirectory = node_path.default.dirname(projectDirectory);
|
|
52
|
+
await (0, node_fs_promises.mkdir)(parentDirectory, { recursive: true });
|
|
53
|
+
const stagingDirectory = await (0, node_fs_promises.mkdtemp)(node_path.default.join(parentDirectory, `.${projectName}-`));
|
|
54
|
+
try {
|
|
55
|
+
await renderTemplate(stagingDirectory, {
|
|
56
|
+
displayName,
|
|
57
|
+
projectName
|
|
58
|
+
});
|
|
59
|
+
await writeGeneratedMetadata(stagingDirectory, config, projectName);
|
|
60
|
+
await (0, node_fs_promises.rename)(stagingDirectory, projectDirectory);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
await (0, node_fs_promises.rm)(stagingDirectory, {
|
|
63
|
+
force: true,
|
|
64
|
+
recursive: true
|
|
65
|
+
});
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
const shouldInstall = options.install === true;
|
|
69
|
+
if (shouldInstall) await (options.installer ?? installWithBun)(projectDirectory);
|
|
70
|
+
return {
|
|
71
|
+
displayName,
|
|
72
|
+
installed: shouldInstall,
|
|
73
|
+
packageId,
|
|
74
|
+
projectDirectory,
|
|
75
|
+
projectName
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function normalizeProjectName(value) {
|
|
79
|
+
const projectName = value.trim().toLowerCase();
|
|
80
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/u.test(projectName) || projectName.length > 214) throw new Error("Project directory name must start with a letter or number and contain only lowercase letters, numbers, dots, hyphens, or underscores.");
|
|
81
|
+
return projectName;
|
|
82
|
+
}
|
|
83
|
+
function normalizeDisplayName(value) {
|
|
84
|
+
const displayName = value.trim().replace(/\s+/gu, " ");
|
|
85
|
+
if (displayName.length === 0 || displayName.length > 80) throw new Error("Application name must contain between 1 and 80 characters.");
|
|
86
|
+
return displayName;
|
|
87
|
+
}
|
|
88
|
+
function defaultPackageId(projectName) {
|
|
89
|
+
return `com.example.${projectName.replace(/[^a-z0-9_]+/gu, "_").replace(/^[^a-z]+/u, "").replace(/_+$/gu, "") || "app"}`;
|
|
90
|
+
}
|
|
91
|
+
function validatePackageId(value) {
|
|
92
|
+
const packageId = value.trim();
|
|
93
|
+
(0, jilatax.parseAppConfig)({ jilatax: {
|
|
94
|
+
android: { package: packageId },
|
|
95
|
+
name: "Package validation"
|
|
96
|
+
} });
|
|
97
|
+
return packageId;
|
|
98
|
+
}
|
|
99
|
+
async function renderTemplate(targetRoot, values) {
|
|
100
|
+
await copyTemplateDirectory((0, node_url.fileURLToPath)(new URL("../template", require("url").pathToFileURL(__filename).href)), targetRoot, "", values);
|
|
101
|
+
}
|
|
102
|
+
async function copyTemplateDirectory(templateRoot, targetRoot, relativeDirectory, values) {
|
|
103
|
+
const entries = await (0, node_fs_promises.readdir)(node_path.default.join(templateRoot, relativeDirectory), { withFileTypes: true });
|
|
104
|
+
for (const entry of entries) {
|
|
105
|
+
const relativeSource = node_path.default.join(relativeDirectory, entry.name);
|
|
106
|
+
const normalizedSource = relativeSource.split(node_path.default.sep).join("/");
|
|
107
|
+
const sourcePath = node_path.default.join(templateRoot, relativeSource);
|
|
108
|
+
if (entry.isSymbolicLink() || !entry.isDirectory() && !entry.isFile()) throw new Error(`Unsupported template entry: ${normalizedSource}`);
|
|
109
|
+
if (entry.isDirectory()) {
|
|
110
|
+
await copyTemplateDirectory(templateRoot, targetRoot, relativeSource, values);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const outputRelative = templateOutputPath(normalizedSource);
|
|
114
|
+
const outputPath = node_path.default.join(targetRoot, ...outputRelative.split("/"));
|
|
115
|
+
await (0, node_fs_promises.mkdir)(node_path.default.dirname(outputPath), { recursive: true });
|
|
116
|
+
if (normalizedSource.endsWith("gradle-wrapper.jar.base64")) {
|
|
117
|
+
const encoded = (await (0, node_fs_promises.readFile)(sourcePath, "utf8")).replace(/\s+/gu, "");
|
|
118
|
+
await (0, node_fs_promises.writeFile)(outputPath, Buffer.from(encoded, "base64"));
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (TEXT_TEMPLATE_FILES.has(normalizedSource)) {
|
|
122
|
+
await (0, node_fs_promises.writeFile)(outputPath, renderTextTemplate(await (0, node_fs_promises.readFile)(sourcePath, "utf8"), values), "utf8");
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (normalizedSource.endsWith(".tmpl")) throw new Error(`Unregistered text template: ${normalizedSource}`);
|
|
126
|
+
await (0, node_fs_promises.copyFile)(sourcePath, outputPath);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function templateOutputPath(relativeSource) {
|
|
130
|
+
if (relativeSource === "gitignore") return ".gitignore";
|
|
131
|
+
if (relativeSource.endsWith("gradle-wrapper.jar.base64")) return relativeSource.slice(0, -7);
|
|
132
|
+
return relativeSource.endsWith(".tmpl") ? relativeSource.slice(0, -5) : relativeSource;
|
|
133
|
+
}
|
|
134
|
+
function renderTextTemplate(source, values) {
|
|
135
|
+
const replacements = /* @__PURE__ */ new Map([
|
|
136
|
+
["{{displayNameJson}}", JSON.stringify(values.displayName)],
|
|
137
|
+
["{{projectName}}", values.projectName],
|
|
138
|
+
["{{projectNameJson}}", JSON.stringify(values.projectName)]
|
|
139
|
+
]);
|
|
140
|
+
let rendered = source;
|
|
141
|
+
for (const [token, replacement] of replacements) rendered = rendered.replaceAll(token, replacement);
|
|
142
|
+
const unresolved = rendered.match(/\{\{[A-Za-z][A-Za-z0-9]*\}\}/u);
|
|
143
|
+
if (unresolved !== null) throw new Error(`Unresolved template token: ${unresolved[0]}`);
|
|
144
|
+
return rendered;
|
|
145
|
+
}
|
|
146
|
+
async function writeGeneratedMetadata(projectRoot, config, projectName) {
|
|
147
|
+
const packageJson = {
|
|
148
|
+
name: projectName,
|
|
149
|
+
version: config.jilatax.version,
|
|
150
|
+
private: true,
|
|
151
|
+
type: "module",
|
|
152
|
+
packageManager: "bun@1.3.4",
|
|
153
|
+
engines: { node: ">=22.18.0" },
|
|
154
|
+
scripts: {
|
|
155
|
+
dev: "rspeedy dev",
|
|
156
|
+
build: "rspeedy build",
|
|
157
|
+
typecheck: "tsc -b",
|
|
158
|
+
"run:android": "jilatax run:android",
|
|
159
|
+
"create:aab": "jilatax create:aab"
|
|
160
|
+
},
|
|
161
|
+
dependencies: {
|
|
162
|
+
"@lynx-js/react": LYNX_REACT_VERSION,
|
|
163
|
+
jilatax: JILATAX_VERSION
|
|
164
|
+
},
|
|
165
|
+
devDependencies: {
|
|
166
|
+
"@jilatax/cli": JILATAX_CLI_VERSION,
|
|
167
|
+
"@lynx-js/react-rsbuild-plugin": REACT_PLUGIN_VERSION,
|
|
168
|
+
"@lynx-js/rspeedy": RSPEEDY_VERSION,
|
|
169
|
+
"@lynx-js/types": LYNX_TYPES_VERSION,
|
|
170
|
+
"@types/node": "^22.20.1",
|
|
171
|
+
"@types/react": "^18.3.20",
|
|
172
|
+
typescript: "~5.9.3"
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
await (0, node_fs_promises.writeFile)(node_path.default.join(projectRoot, "package.json"), `${JSON.stringify(packageJson, null, 2)}\n`, "utf8");
|
|
176
|
+
await (0, node_fs_promises.writeFile)(node_path.default.join(projectRoot, "app.json"), `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
177
|
+
await (0, node_fs_promises.writeFile)(node_path.default.join(projectRoot, "android", "jilatax.properties"), (0, jilatax.serializeAndroidProjectConfig)(config), "utf8");
|
|
178
|
+
if (process.platform !== "win32") await (0, node_fs_promises.chmod)(node_path.default.join(projectRoot, "android", "gradlew"), 493);
|
|
179
|
+
}
|
|
180
|
+
function createInitialConfig(displayName, projectName, packageId) {
|
|
181
|
+
const slug = projectName.replace(/[._-]+/gu, "-").replace(/^-|-$/gu, "");
|
|
182
|
+
const schemeBase = projectName.replace(/[^a-z0-9+.-]+/gu, "-").replace(/^[^a-z]+/u, "");
|
|
183
|
+
return (0, jilatax.parseAppConfig)({
|
|
184
|
+
$schema: "./node_modules/jilatax/schema/app.schema.json",
|
|
185
|
+
jilatax: {
|
|
186
|
+
android: {
|
|
187
|
+
package: packageId,
|
|
188
|
+
predictiveBackGestureEnabled: false,
|
|
189
|
+
versionCode: 1
|
|
190
|
+
},
|
|
191
|
+
name: displayName,
|
|
192
|
+
orientation: "portrait",
|
|
193
|
+
scheme: schemeBase || "jilatax-app",
|
|
194
|
+
slug,
|
|
195
|
+
splash: { backgroundColor: "#0F172A" },
|
|
196
|
+
userInterfaceStyle: "automatic",
|
|
197
|
+
version: "1.0.0"
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
async function assertTargetDoesNotExist(target) {
|
|
202
|
+
try {
|
|
203
|
+
await (0, node_fs_promises.lstat)(target);
|
|
204
|
+
} catch (error) {
|
|
205
|
+
if (error.code === "ENOENT") return;
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
throw new Error(`Target directory already exists: ${target}`);
|
|
209
|
+
}
|
|
210
|
+
function titleCase$1(value) {
|
|
211
|
+
return value.split(/[-_.\s]+/u).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
|
|
212
|
+
}
|
|
213
|
+
function installWithBun(projectDirectory) {
|
|
214
|
+
return new Promise((resolveInstall, rejectInstall) => {
|
|
215
|
+
const child = (0, node_child_process.spawn)("bun", ["install"], {
|
|
216
|
+
cwd: projectDirectory,
|
|
217
|
+
env: process.env,
|
|
218
|
+
stdio: "inherit",
|
|
219
|
+
windowsHide: true
|
|
220
|
+
});
|
|
221
|
+
child.once("error", rejectInstall);
|
|
222
|
+
child.once("close", (code) => {
|
|
223
|
+
if (code === 0) {
|
|
224
|
+
resolveInstall();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
rejectInstall(/* @__PURE__ */ new Error(`bun install failed with exit code ${code ?? 1}.`));
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region src/cli.ts
|
|
233
|
+
var PromptCancelledError = class extends Error {};
|
|
234
|
+
async function runCreateCli(args = process.argv.slice(2), services = {}) {
|
|
235
|
+
const log = services.log ?? console.log;
|
|
236
|
+
const warn = services.warn ?? console.error;
|
|
237
|
+
try {
|
|
238
|
+
const parsed = parseArgs(args);
|
|
239
|
+
if (parsed.showHelp) {
|
|
240
|
+
log(createHelpText());
|
|
241
|
+
return 0;
|
|
242
|
+
}
|
|
243
|
+
if (parsed.showVersion) {
|
|
244
|
+
log(await readCreatorVersion());
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
const options = services.interactive ?? (node_process.stdin.isTTY && node_process.stdout.isTTY) ? await promptForOptions(parsed) : nonInteractiveOptions(parsed);
|
|
248
|
+
printResult(await (services.create ?? createProject)({
|
|
249
|
+
displayName: options.displayName,
|
|
250
|
+
install: options.install,
|
|
251
|
+
packageId: options.packageId,
|
|
252
|
+
targetDirectory: options.targetDirectory
|
|
253
|
+
}), log);
|
|
254
|
+
return 0;
|
|
255
|
+
} catch (error) {
|
|
256
|
+
if (error instanceof PromptCancelledError) return 0;
|
|
257
|
+
warn(error instanceof Error ? error.message : String(error));
|
|
258
|
+
return 1;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function createHelpText() {
|
|
262
|
+
return `Create an Android-first Jilatax application.
|
|
263
|
+
|
|
264
|
+
Usage:
|
|
265
|
+
create-jilatax [directory] [options]
|
|
266
|
+
|
|
267
|
+
Options:
|
|
268
|
+
--name <display-name> Human-readable application name
|
|
269
|
+
--package-id <id> Android application ID
|
|
270
|
+
--install Install dependencies with Bun
|
|
271
|
+
--skip-install Generate without installing dependencies
|
|
272
|
+
-h, --help Show this help
|
|
273
|
+
-V, --version Show the installed creator version`;
|
|
274
|
+
}
|
|
275
|
+
function parseArgs(args) {
|
|
276
|
+
let displayName;
|
|
277
|
+
let install;
|
|
278
|
+
let packageId;
|
|
279
|
+
let showHelp = false;
|
|
280
|
+
let showVersion = false;
|
|
281
|
+
let targetDirectory;
|
|
282
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
283
|
+
const argument = args[index];
|
|
284
|
+
if (argument === void 0) continue;
|
|
285
|
+
if (argument === "--install") install = true;
|
|
286
|
+
else if (argument === "--skip-install" || argument === "--no-install") install = false;
|
|
287
|
+
else if (argument === "--name") {
|
|
288
|
+
displayName = readOptionValue(args, index, argument);
|
|
289
|
+
index += 1;
|
|
290
|
+
} else if (argument.startsWith("--name=")) displayName = readInlineValue(argument, "--name");
|
|
291
|
+
else if (argument === "--package-id") {
|
|
292
|
+
packageId = readOptionValue(args, index, argument);
|
|
293
|
+
index += 1;
|
|
294
|
+
} else if (argument.startsWith("--package-id=")) packageId = readInlineValue(argument, "--package-id");
|
|
295
|
+
else if (argument === "--help" || argument === "-h") showHelp = true;
|
|
296
|
+
else if (argument === "--version" || argument === "-V") showVersion = true;
|
|
297
|
+
else if (argument.startsWith("-")) throw new Error(`Unknown option: ${argument}`);
|
|
298
|
+
else if (targetDirectory === void 0) targetDirectory = argument;
|
|
299
|
+
else throw new Error(`Unexpected argument: ${argument}`);
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
...displayName === void 0 ? {} : { displayName },
|
|
303
|
+
...install === void 0 ? {} : { install },
|
|
304
|
+
...packageId === void 0 ? {} : { packageId },
|
|
305
|
+
showHelp,
|
|
306
|
+
showVersion,
|
|
307
|
+
...targetDirectory === void 0 ? {} : { targetDirectory }
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
async function promptForOptions(options) {
|
|
311
|
+
_clack_prompts.intro("Jilatax ยท Android-first Lynx application");
|
|
312
|
+
const targetDirectory = options.targetDirectory ?? unwrapPrompt(await _clack_prompts.text({
|
|
313
|
+
message: "Where should the project be created?",
|
|
314
|
+
placeholder: "./my-jilatax-app",
|
|
315
|
+
validate(value) {
|
|
316
|
+
try {
|
|
317
|
+
normalizeProjectNameFromDirectory(value ?? "");
|
|
318
|
+
} catch (error) {
|
|
319
|
+
return validationMessage(error);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}));
|
|
323
|
+
const projectName = normalizeProjectNameFromDirectory(targetDirectory);
|
|
324
|
+
const displayName = options.displayName ?? unwrapPrompt(await _clack_prompts.text({
|
|
325
|
+
initialValue: titleCase(projectName),
|
|
326
|
+
message: "Application name",
|
|
327
|
+
validate(value) {
|
|
328
|
+
try {
|
|
329
|
+
normalizeDisplayName(value ?? "");
|
|
330
|
+
} catch (error) {
|
|
331
|
+
return validationMessage(error);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}));
|
|
335
|
+
const packageId = options.packageId ?? unwrapPrompt(await _clack_prompts.text({
|
|
336
|
+
initialValue: defaultPackageId(projectName),
|
|
337
|
+
message: "Android application ID",
|
|
338
|
+
validate(value) {
|
|
339
|
+
try {
|
|
340
|
+
validatePackageId(value ?? "");
|
|
341
|
+
} catch (error) {
|
|
342
|
+
return validationMessage(error);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}));
|
|
346
|
+
const install = options.install ?? unwrapPrompt(await _clack_prompts.confirm({
|
|
347
|
+
initialValue: true,
|
|
348
|
+
message: "Install dependencies with Bun?"
|
|
349
|
+
}));
|
|
350
|
+
return {
|
|
351
|
+
displayName: normalizeDisplayName(displayName),
|
|
352
|
+
install,
|
|
353
|
+
packageId: validatePackageId(packageId),
|
|
354
|
+
targetDirectory
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function nonInteractiveOptions(options) {
|
|
358
|
+
if (options.targetDirectory === void 0) throw new Error("Project directory is required in a non-interactive terminal.");
|
|
359
|
+
const projectName = normalizeProjectNameFromDirectory(options.targetDirectory);
|
|
360
|
+
return {
|
|
361
|
+
displayName: normalizeDisplayName(options.displayName ?? titleCase(projectName)),
|
|
362
|
+
install: options.install ?? false,
|
|
363
|
+
packageId: validatePackageId(options.packageId ?? defaultPackageId(projectName)),
|
|
364
|
+
targetDirectory: options.targetDirectory
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
function normalizeProjectNameFromDirectory(directory) {
|
|
368
|
+
return normalizeProjectName(directory.trim().replace(/[\\/]+$/gu, "").split(/[\\/]/u).at(-1) ?? "");
|
|
369
|
+
}
|
|
370
|
+
function unwrapPrompt(value) {
|
|
371
|
+
if (_clack_prompts.isCancel(value)) {
|
|
372
|
+
_clack_prompts.cancel("Project creation cancelled.");
|
|
373
|
+
throw new PromptCancelledError();
|
|
374
|
+
}
|
|
375
|
+
return value;
|
|
376
|
+
}
|
|
377
|
+
function validationMessage(error) {
|
|
378
|
+
return error instanceof Error ? error.message : String(error);
|
|
379
|
+
}
|
|
380
|
+
function readOptionValue(args, index, option) {
|
|
381
|
+
const value = args[index + 1];
|
|
382
|
+
if (value === void 0 || value.startsWith("-") || value.trim().length === 0) throw new Error(`${option} requires a value.`);
|
|
383
|
+
return value.trim();
|
|
384
|
+
}
|
|
385
|
+
function readInlineValue(argument, option) {
|
|
386
|
+
const value = argument.slice(`${option}=`.length).trim();
|
|
387
|
+
if (value.length === 0) throw new Error(`${option} requires a value.`);
|
|
388
|
+
return value;
|
|
389
|
+
}
|
|
390
|
+
function titleCase(value) {
|
|
391
|
+
return value.split(/[-_.\s]+/u).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
|
|
392
|
+
}
|
|
393
|
+
function printResult(result, log) {
|
|
394
|
+
const installStep = result.installed ? "" : " bun install\n";
|
|
395
|
+
log(`Created ${result.displayName} in ${result.projectDirectory}.
|
|
396
|
+
|
|
397
|
+
Next steps:
|
|
398
|
+
cd ${JSON.stringify(result.projectDirectory)}
|
|
399
|
+
${installStep} bun run run:android
|
|
400
|
+
|
|
401
|
+
Release bundle:
|
|
402
|
+
bun run create:aab`);
|
|
403
|
+
}
|
|
404
|
+
async function readCreatorVersion() {
|
|
405
|
+
const packageJson = JSON.parse(await (0, node_fs_promises.readFile)(new URL("../package.json", require("url").pathToFileURL(__filename).href), "utf8"));
|
|
406
|
+
return typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
407
|
+
}
|
|
408
|
+
//#endregion
|
|
409
|
+
Object.defineProperty(exports, "createHelpText", {
|
|
410
|
+
enumerable: true,
|
|
411
|
+
get: function() {
|
|
412
|
+
return createHelpText;
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
Object.defineProperty(exports, "createProject", {
|
|
416
|
+
enumerable: true,
|
|
417
|
+
get: function() {
|
|
418
|
+
return createProject;
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
Object.defineProperty(exports, "defaultPackageId", {
|
|
422
|
+
enumerable: true,
|
|
423
|
+
get: function() {
|
|
424
|
+
return defaultPackageId;
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
Object.defineProperty(exports, "normalizeDisplayName", {
|
|
428
|
+
enumerable: true,
|
|
429
|
+
get: function() {
|
|
430
|
+
return normalizeDisplayName;
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
Object.defineProperty(exports, "normalizeProjectName", {
|
|
434
|
+
enumerable: true,
|
|
435
|
+
get: function() {
|
|
436
|
+
return normalizeProjectName;
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
Object.defineProperty(exports, "runCreateCli", {
|
|
440
|
+
enumerable: true,
|
|
441
|
+
get: function() {
|
|
442
|
+
return runCreateCli;
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
Object.defineProperty(exports, "validatePackageId", {
|
|
446
|
+
enumerable: true,
|
|
447
|
+
get: function() {
|
|
448
|
+
return validatePackageId;
|
|
449
|
+
}
|
|
450
|
+
});
|