import { isEmptyOrSpaces, log } from "builder-util"
import { Nullish } from "builder-util-runtime"
import { sanitizeFileName } from "builder-util/out/filename"
import { prerelease } from "semver"
import { PlatformSpecificBuildOptions } from "./options/PlatformSpecificBuildOptions"
import { Packager } from "./packager"
import { expandMacro } from "./util/macroExpander"
export function smarten(s: string): string {
s = s.replace(/(^|[-\u2014\s(["])'/g, "$1\u2018")
s = s.replace(/'/g, "\u2019")
s = s.replace(/(^|[-\u2014/[(\u2018\s])"/g, "$1\u201c")
s = s.replace(/"/g, "\u201d")
return s
}
export class AppInfo {
readonly description = smarten(this.info.metadata.description || "")
readonly version: string
readonly type: string | undefined
readonly shortVersion: string | undefined
readonly shortVersionWindows: string | undefined
readonly buildNumber: string | undefined
readonly buildVersion: string
readonly productName: string
readonly sanitizedProductName: string
readonly productFilename: string
constructor(
private readonly info: Packager,
buildVersion: string | Nullish,
private readonly platformSpecificOptions: PlatformSpecificBuildOptions | null = null,
normalizeNfd = false
) {
this.version = info.metadata.version!
this.type = info.metadata.type
if (buildVersion == null) {
buildVersion = info.config.buildVersion
}
const buildNumberEnvs =
process.env.BUILD_NUMBER ||
process.env.TRAVIS_BUILD_NUMBER ||
process.env.APPVEYOR_BUILD_NUMBER ||
process.env.CIRCLE_BUILD_NUM ||
process.env.BUILD_BUILDNUMBER ||
process.env.CI_PIPELINE_IID
this.buildNumber = info.config.buildNumber || buildNumberEnvs
if (buildVersion == null) {
buildVersion = this.version
if (!isEmptyOrSpaces(this.buildNumber)) {
buildVersion += `.${this.buildNumber}`
}
}
this.buildVersion = buildVersion
if (info.metadata.shortVersion) {
this.shortVersion = info.metadata.shortVersion
}
if (info.metadata.shortVersionWindows) {
this.shortVersionWindows = info.metadata.shortVersionWindows
}
this.productName = info.config.productName || info.metadata.productName || info.metadata.name!
this.sanitizedProductName = sanitizeFileName(this.productName, normalizeNfd)
const executableName = platformSpecificOptions?.executableName ?? info.config.executableName
this.productFilename = executableName != null ? sanitizeFileName(executableName, normalizeNfd) : this.sanitizedProductName
}
get channel(): string | null {
const prereleaseInfo = prerelease(this.version)
if (prereleaseInfo != null && prereleaseInfo.length > 0) {
return prereleaseInfo[0] as string | null
}
return null
}
getVersionInWeirdWindowsForm(isSetBuildNumber = true): string {
const [major, maybe_minor, maybe_patch] = this.version.split(".").map(versionPart => parseInt(versionPart))
if (isNaN(major)) {
throw new Error(`Invalid major number in: ${this.version}`)
}
const minor = maybe_minor ?? 0
const patch = maybe_patch ?? 0
if (isNaN(minor) || isNaN(patch)) {
throw new Error(`Invalid minor or patch number in: ${this.version}`)
}
let buildNumber = isSetBuildNumber ? this.buildNumber : null
if (buildNumber == null || !/^\d+$/.test(buildNumber)) {
buildNumber = "0"
}
return `${major}.${minor}.${patch}.${buildNumber}`
}
private get notNullDevMetadata() {
return this.info.devMetadata || {}
}
get companyName(): string | null {
const author = this.info.metadata.author || this.notNullDevMetadata.author
return author == null ? null : author.name
}
get id(): string {
let appId: string | Nullish = null
for (const options of [this.platformSpecificOptions, this.info.config]) {
if (options != null && appId == null) {
appId = options.appId
}
}
const generateDefaultAppId = () => {
const info = this.info
return `${info.framework.defaultAppIdPrefix}${info.metadata.name!.toLowerCase()}`
}
if (appId != null && (appId === "your.id" || isEmptyOrSpaces(appId))) {
const incorrectAppId = appId
appId = generateDefaultAppId()
log.warn(`do not use "${incorrectAppId}" as appId, "${appId}" will be used instead`)
}
return appId == null ? generateDefaultAppId() : appId
}
get macBundleIdentifier(): string {
return filterCFBundleIdentifier(this.id)
}
get name(): string {
return this.info.metadata.name!
}
get linuxPackageName(): string {
const name = this.name
return name.startsWith("@") ? this.sanitizedProductName : name
}
get sanitizedName(): string {
return sanitizeFileName(this.name)
}
get updaterCacheDirName(): string {
return this.sanitizedName.toLowerCase() + "-updater"
}
get copyright(): string {
const copyright = this.info.config.copyright
if (copyright != null) {
return expandMacro(copyright, null, this)
}
return `Copyright © ${new Date().getFullYear()} ${this.companyName || this.productName}`
}
async computePackageUrl(): Promise<string | null> {
const url = this.info.metadata.homepage || this.notNullDevMetadata.homepage
if (url != null) {
return url
}
const info = await this.info.repositoryInfo
return info == null || info.type !== "github" ? null : `https://${info.domain}/${info.user}/${info.project}`
}
}
export function filterCFBundleIdentifier(identifier: string) {
return identifier.replace(/ /g, "-").replace(/[^a-zA-Z0-9.-]/g, "")
}