///|
fn package_report_text(
options : PackageReportOptions,
) -> Result[String, String] {
let manifest = match read_project_manifest(options.project_root) {
Ok(value) => value
Err(message) => return Err(message)
}
let builder = StringBuilder()
builder.write_string("MoUI package report\n")
builder.write_string("projectRoot: " + options.project_root + "\n")
builder.write_string("id: " + manifest.id + "\n")
builder.write_string("module: " + manifest.module_name + "\n")
builder.write_string("title: " + manifest.title + "\n")
builder.write_string("mouiVersion: " + manifest.moui_version + "\n")
builder.write_string("platforms: " + manifest.platforms.join(", ") + "\n")
match manifest.bundle_id {
Some(value) => builder.write_string("bundleId: " + value + "\n")
None => builder.write_string("bundleId: (none)\n")
}
let mobile_path = join_path(options.project_root, "moui.mobile.json")
if @fs.path_exists(mobile_path) {
let text = @fs.read_file_to_string(mobile_path) catch {
_ => return Err("could not read moui.mobile.json: " + mobile_path)
}
let parsed = @json.parse(text) catch {
_ => return Err("moui.mobile.json is invalid JSON")
}
guard parsed is Object(root) else {
return Err("moui.mobile.json root must be an object")
}
match root.get("schemaVersion") {
Some(Number(value, repr=_)) =>
builder.write_string(
"mobileConfigSchemaVersion: " + value.to_int().to_string() + "\n",
)
_ => builder.write_string("mobileConfigSchemaVersion: (missing)\n")
}
} else {
builder.write_string("moui.mobile.json: (absent)\n")
}
Ok(builder.to_string())
}
///|
fn package_report_json(
options : PackageReportOptions,
) -> Result[String, String] {
let manifest = match read_project_manifest(options.project_root) {
Ok(value) => value
Err(message) => return Err(message)
}
let root : Map[String, Json] = Map([
("schemaVersion", (1 : Int).to_json()),
("ok", true.to_json()),
("command", "package".to_json()),
("projectRoot", options.project_root.to_json()),
("id", manifest.id.to_json()),
("module", manifest.module_name.to_json()),
("title", manifest.title.to_json()),
("mouiVersion", manifest.moui_version.to_json()),
("platforms", manifest.platforms.to_json()),
])
match manifest.bundle_id {
Some(value) => root["bundleId"] = value.to_json()
None => ()
}
let mobile_path = join_path(options.project_root, "moui.mobile.json")
if @fs.path_exists(mobile_path) {
let text = @fs.read_file_to_string(mobile_path) catch {
_ => return Err("could not read moui.mobile.json: " + mobile_path)
}
let parsed = @json.parse(text) catch {
_ => return Err("moui.mobile.json is invalid JSON")
}
root["mobile"] = parsed
}
Ok(Json::object(root).stringify(indent=2) + "\n")
}
///|
fn package_output_kind(platform : String) -> String {
match platform {
"web" => "web-bundle"
"macos" => "app-bundle"
"windows" => "portable-directory"
"linux" => "native-bundle"
"android" => "apk"
"ios" => "app-bundle"
"harmonyos" => "hap"
_ => "unknown"
}
}
///|
fn package_platform_is_supported(platform : String) -> Bool {
["web", "macos", "windows", "linux", "android", "ios", "harmonyos"].contains(
platform,
)
}
///|
fn package_manifest_json(
options : PackageReportOptions,
) -> Result[Json, String] {
let project = match read_project_manifest(options.project_root) {
Ok(value) => value
Err(message) => return Err(message)
}
let platforms = if options.platforms.is_empty() {
project.platforms.copy()
} else {
options.platforms.copy()
}
let seen : Map[String, Bool] = Map([])
for platform in platforms {
if !project.platforms.contains(platform) {
return Err("platform is not declared by project: " + platform)
}
if !package_platform_is_supported(platform) {
return Err("unsupported package platform: " + platform)
}
if seen.contains(platform) {
return Err("duplicate package platform: " + platform)
}
seen[platform] = true
}
let version = match options.version {
Some(value) if value != "" => value
Some(_) => return Err("--version must not be empty")
None => project.moui_version
}
let output_root = match options.output_dir {
Some(value) if value != "" => value
Some(_) => return Err("--output must not be empty")
None => join_path(options.project_root, "dist")
}
let artifacts : Array[Json] = []
for platform in platforms {
let output_path = join_path(output_root, platform)
artifacts.push(
Json::object(
Map([
("platform", platform.to_json()),
("outputKind", package_output_kind(platform).to_json()),
("outputPath", output_path.to_json()),
("buildRequested", options.build.to_json()),
(
"signing",
Json::object(
Map([
("status", "host-required".to_json()),
("signature", Json::null()),
]),
),
),
(
"notarization",
Json::object(Map([("status", "host-required".to_json())])),
),
(
"installer",
Json::object(Map([("status", "host-specific".to_json())])),
),
(
"updateManifest",
Json::object(Map([("status", "not-configured".to_json())])),
),
]),
),
)
}
Ok(
Json::object(
Map([
("schemaVersion", (1 : Int).to_json()),
("ok", true.to_json()),
("command", "package".to_json()),
(
"status",
(if options.dry_run {
"dry-run"
} else if options.build {
"build-requested"
} else {
"planned"
}).to_json(),
),
("projectRoot", options.project_root.to_json()),
(
"project",
Json::object(
Map([
("id", project.id.to_json()),
("title", project.title.to_json()),
("module", project.module_name.to_json()),
("mouiVersion", project.moui_version.to_json()),
(
"bundleId",
match project.bundle_id {
Some(value) => value.to_json()
None => Json::null()
},
),
]),
),
),
("version", version.to_json()),
("platforms", platforms.to_json()),
("artifacts", artifacts.to_json()),
(
"release",
Json::object(
Map([
("signing", "host-required".to_json()),
("notarization", "host-required".to_json()),
("installers", "host-specific".to_json()),
("updateManifest", "not-configured".to_json()),
]),
),
),
]),
),
)
}
///|
fn package_manifest_outcome(
options : PackageReportOptions,
json : Bool,
) -> Result[CliOutcome, String] {
let manifest = match package_manifest_json(options) {
Ok(value) => value
Err(message) => return Err(message)
}
let text = manifest.stringify(indent=2) + "\n"
match options.manifest_path {
Some(path) if !options.dry_run => {
match mkdir_p(parent_dir(path)) {
Ok(_) => ()
Err(message) => return Err(message)
}
match write_file_bytes_result(path, @utf8.encode(text[:], bom=false)) {
Ok(_) => ()
Err(message) => return Err(message)
}
}
_ => ()
}
let output = if json {
text
} else {
let status = match manifest {
Object(fields) =>
match fields.get("status") {
Some(String(value)) => value
_ => "planned"
}
_ => "planned"
}
let builder = StringBuilder()
builder.write_string("MoUI package manifest (" + status + ")\n")
builder.write_string("projectRoot: " + options.project_root + "\n")
builder.write_string(
"manifest: " + options.manifest_path.unwrap_or("(stdout)") + "\n",
)
builder.write_string("artifacts:\n")
match manifest {
Object(fields) =>
match fields.get("artifacts") {
Some(Array(items)) =>
for item in items {
match item {
Object(values) =>
builder.write_string(
"- " +
values
.get("platform")
.map_or("unknown", item => item.stringify()) +
" -> " +
values
.get("outputPath")
.map_or("", item => item.stringify()) +
"\n",
)
_ => ()
}
}
_ => ()
}
_ => ()
}
builder.to_string()
}
Ok(stdout_outcome(output))
}