mirror of
https://gitee.com/myxzgzs/boyue_jnpf.git
synced 2025-08-11 00:32:45 +08:00
381 lines
19 KiB
JavaScript
381 lines
19 KiB
JavaScript
![]() |
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
const builder_util_1 = require("builder-util");
|
||
|
const fs_extra_1 = require("fs-extra");
|
||
|
const path = require("path");
|
||
|
const windowsSignToolManager_1 = require("../codeSign/windowsSignToolManager");
|
||
|
const core_1 = require("../core");
|
||
|
const pathManager_1 = require("../util/pathManager");
|
||
|
const targetUtil_1 = require("./targetUtil");
|
||
|
const APPX_ASSETS_DIR_NAME = "appx";
|
||
|
const vendorAssetsForDefaultAssets = {
|
||
|
"StoreLogo.png": "SampleAppx.50x50.png",
|
||
|
"Square150x150Logo.png": "SampleAppx.150x150.png",
|
||
|
"Square44x44Logo.png": "SampleAppx.44x44.png",
|
||
|
"Wide310x150Logo.png": "SampleAppx.310x150.png",
|
||
|
};
|
||
|
const restrictedApplicationIdValues = [
|
||
|
"CON",
|
||
|
"PRN",
|
||
|
"AUX",
|
||
|
"NUL",
|
||
|
"COM1",
|
||
|
"COM2",
|
||
|
"COM3",
|
||
|
"COM4",
|
||
|
"COM5",
|
||
|
"COM6",
|
||
|
"COM7",
|
||
|
"COM8",
|
||
|
"COM9",
|
||
|
"LPT1",
|
||
|
"LPT2",
|
||
|
"LPT3",
|
||
|
"LPT4",
|
||
|
"LPT5",
|
||
|
"LPT6",
|
||
|
"LPT7",
|
||
|
"LPT8",
|
||
|
"LPT9",
|
||
|
];
|
||
|
const DEFAULT_RESOURCE_LANG = "en-US";
|
||
|
class AppXTarget extends core_1.Target {
|
||
|
constructor(packager, outDir) {
|
||
|
super("appx");
|
||
|
this.packager = packager;
|
||
|
this.outDir = outDir;
|
||
|
this.options = (0, builder_util_1.deepAssign)({}, this.packager.platformSpecificBuildOptions, this.packager.config.appx);
|
||
|
if (process.platform !== "darwin" && (process.platform !== "win32" || (0, windowsSignToolManager_1.isOldWin6)())) {
|
||
|
throw new Error("AppX is supported only on Windows 10 or Windows Server 2012 R2 (version number 6.3+)");
|
||
|
}
|
||
|
}
|
||
|
// https://docs.microsoft.com/en-us/windows/uwp/packaging/create-app-package-with-makeappx-tool#mapping-files
|
||
|
async build(appOutDir, arch) {
|
||
|
const packager = this.packager;
|
||
|
const artifactName = packager.expandArtifactBeautyNamePattern(this.options, "appx", arch);
|
||
|
const artifactPath = path.join(this.outDir, artifactName);
|
||
|
await packager.info.emitArtifactBuildStarted({
|
||
|
targetPresentableName: "AppX",
|
||
|
file: artifactPath,
|
||
|
arch,
|
||
|
});
|
||
|
const vendorPath = await (0, windowsSignToolManager_1.getSignVendorPath)();
|
||
|
const vm = await packager.vm.value;
|
||
|
const stageDir = await (0, targetUtil_1.createStageDir)(this, packager, arch);
|
||
|
const mappingFile = stageDir.getTempFile("mapping.txt");
|
||
|
const makeAppXArgs = ["pack", "/o" /* overwrite the output file if it exists */, "/f", vm.toVmFile(mappingFile), "/p", vm.toVmFile(artifactPath)];
|
||
|
if (packager.compression === "store") {
|
||
|
makeAppXArgs.push("/nc");
|
||
|
}
|
||
|
const mappingList = [];
|
||
|
mappingList.push(await Promise.all((await (0, builder_util_1.walk)(appOutDir)).map(file => {
|
||
|
let appxPath = file.substring(appOutDir.length + 1);
|
||
|
if (path.sep !== "\\") {
|
||
|
appxPath = appxPath.replace(/\//g, "\\");
|
||
|
}
|
||
|
return `"${vm.toVmFile(file)}" "app\\${appxPath}"`;
|
||
|
})));
|
||
|
const userAssetDir = await this.packager.getResource(undefined, APPX_ASSETS_DIR_NAME);
|
||
|
const assetInfo = await AppXTarget.computeUserAssets(vm, vendorPath, userAssetDir);
|
||
|
const userAssets = assetInfo.userAssets;
|
||
|
const manifestFile = stageDir.getTempFile("AppxManifest.xml");
|
||
|
await this.writeManifest(manifestFile, arch, await this.computePublisherName(), userAssets);
|
||
|
await packager.info.emitAppxManifestCreated(manifestFile);
|
||
|
mappingList.push(assetInfo.mappings);
|
||
|
mappingList.push([`"${vm.toVmFile(manifestFile)}" "AppxManifest.xml"`]);
|
||
|
const signToolArch = arch === builder_util_1.Arch.arm64 ? "x64" : builder_util_1.Arch[arch];
|
||
|
if (isScaledAssetsProvided(userAssets)) {
|
||
|
const outFile = vm.toVmFile(stageDir.getTempFile("resources.pri"));
|
||
|
const makePriPath = vm.toVmFile(path.join(vendorPath, "windows-10", signToolArch, "makepri.exe"));
|
||
|
const assetRoot = stageDir.getTempFile("appx/assets");
|
||
|
await (0, fs_extra_1.emptyDir)(assetRoot);
|
||
|
await Promise.all(assetInfo.allAssets.map(it => (0, builder_util_1.copyOrLinkFile)(it, path.join(assetRoot, path.basename(it)))));
|
||
|
await vm.exec(makePriPath, [
|
||
|
"new",
|
||
|
"/Overwrite",
|
||
|
"/Manifest",
|
||
|
vm.toVmFile(manifestFile),
|
||
|
"/ProjectRoot",
|
||
|
vm.toVmFile(path.dirname(assetRoot)),
|
||
|
"/ConfigXml",
|
||
|
vm.toVmFile(path.join((0, pathManager_1.getTemplatePath)("appx"), "priconfig.xml")),
|
||
|
"/OutputFile",
|
||
|
outFile,
|
||
|
]);
|
||
|
// in addition to resources.pri, resources.scale-140.pri and other such files will be generated
|
||
|
for (const resourceFile of (await (0, fs_extra_1.readdir)(stageDir.dir)).filter(it => it.startsWith("resources.")).sort()) {
|
||
|
mappingList.push([`"${vm.toVmFile(stageDir.getTempFile(resourceFile))}" "${resourceFile}"`]);
|
||
|
}
|
||
|
makeAppXArgs.push("/l");
|
||
|
}
|
||
|
let mapping = "[Files]";
|
||
|
for (const list of mappingList) {
|
||
|
mapping += "\r\n" + list.join("\r\n");
|
||
|
}
|
||
|
await (0, fs_extra_1.writeFile)(mappingFile, mapping);
|
||
|
packager.debugLogger.add("appx.mapping", mapping);
|
||
|
if (this.options.makeappxArgs != null) {
|
||
|
makeAppXArgs.push(...this.options.makeappxArgs);
|
||
|
}
|
||
|
await vm.exec(vm.toVmFile(path.join(vendorPath, "windows-10", signToolArch, "makeappx.exe")), makeAppXArgs);
|
||
|
await packager.sign(artifactPath);
|
||
|
await stageDir.cleanup();
|
||
|
await packager.info.emitArtifactBuildCompleted({
|
||
|
file: artifactPath,
|
||
|
packager,
|
||
|
arch,
|
||
|
safeArtifactName: packager.computeSafeArtifactName(artifactName, "appx"),
|
||
|
target: this,
|
||
|
isWriteUpdateInfo: this.options.electronUpdaterAware,
|
||
|
});
|
||
|
}
|
||
|
static async computeUserAssets(vm, vendorPath, userAssetDir) {
|
||
|
const mappings = [];
|
||
|
let userAssets;
|
||
|
const allAssets = [];
|
||
|
if (userAssetDir == null) {
|
||
|
userAssets = [];
|
||
|
}
|
||
|
else {
|
||
|
userAssets = (await (0, fs_extra_1.readdir)(userAssetDir)).filter(it => !it.startsWith(".") && !it.endsWith(".db") && it.includes("."));
|
||
|
for (const name of userAssets) {
|
||
|
mappings.push(`"${vm.toVmFile(userAssetDir)}${vm.pathSep}${name}" "assets\\${name}"`);
|
||
|
allAssets.push(path.join(userAssetDir, name));
|
||
|
}
|
||
|
}
|
||
|
for (const defaultAsset of Object.keys(vendorAssetsForDefaultAssets)) {
|
||
|
if (userAssets.length === 0 || !isDefaultAssetIncluded(userAssets, defaultAsset)) {
|
||
|
const file = path.join(vendorPath, "appxAssets", vendorAssetsForDefaultAssets[defaultAsset]);
|
||
|
mappings.push(`"${vm.toVmFile(file)}" "assets\\${defaultAsset}"`);
|
||
|
allAssets.push(file);
|
||
|
}
|
||
|
}
|
||
|
// we do not use process.arch to build path to tools, because even if you are on x64, ia32 appx tool must be used if you build appx for ia32
|
||
|
return { userAssets, mappings, allAssets };
|
||
|
}
|
||
|
// https://github.com/electron-userland/electron-builder/issues/2108#issuecomment-333200711
|
||
|
async computePublisherName() {
|
||
|
const signtoolManager = await this.packager.signingManager.value;
|
||
|
return signtoolManager.computePublisherName(this, this.options.publisher);
|
||
|
}
|
||
|
async writeManifest(outFile, arch, publisher, userAssets) {
|
||
|
const appInfo = this.packager.appInfo;
|
||
|
const options = this.options;
|
||
|
const executable = `app\\${appInfo.productFilename}.exe`;
|
||
|
const displayName = options.displayName || appInfo.productName;
|
||
|
const extensions = await this.getExtensions(executable, displayName);
|
||
|
const archSpecificMinVersion = arch === builder_util_1.Arch.arm64 ? "10.0.16299.0" : "10.0.14316.0";
|
||
|
const customManifestPath = await this.packager.getResource(this.options.customManifestPath);
|
||
|
if (customManifestPath) {
|
||
|
builder_util_1.log.info({ manifestPath: builder_util_1.log.filePath(customManifestPath) }, "custom appx manifest found");
|
||
|
}
|
||
|
const manifestFileContent = await (0, fs_extra_1.readFile)(customManifestPath || path.join((0, pathManager_1.getTemplatePath)("appx"), "appxmanifest.xml"), "utf8");
|
||
|
const manifest = manifestFileContent.replace(/\${([a-zA-Z0-9]+)}/g, (match, p1) => {
|
||
|
switch (p1) {
|
||
|
case "publisher":
|
||
|
return publisher;
|
||
|
case "publisherDisplayName": {
|
||
|
const name = options.publisherDisplayName || appInfo.companyName;
|
||
|
if (name == null) {
|
||
|
throw new builder_util_1.InvalidConfigurationError(`Please specify "author" in the application package.json — it is required because "appx.publisherDisplayName" is not set.`);
|
||
|
}
|
||
|
return name;
|
||
|
}
|
||
|
case "version":
|
||
|
return appInfo.getVersionInWeirdWindowsForm(options.setBuildNumber === true);
|
||
|
case "applicationId": {
|
||
|
const validCharactersRegex = /^([A-Za-z][A-Za-z0-9]*)(\.[A-Za-z][A-Za-z0-9]*)*$/;
|
||
|
const identitynumber = parseInt(options.identityName, 10) || NaN;
|
||
|
let result;
|
||
|
if (options.applicationId) {
|
||
|
result = options.applicationId;
|
||
|
}
|
||
|
else if (!isNaN(identitynumber) && options.identityName !== null && options.identityName !== undefined) {
|
||
|
if (options.identityName[0] === "0") {
|
||
|
builder_util_1.log.warn(`Remove the 0${identitynumber}`);
|
||
|
result = options.identityName.replace("0" + identitynumber.toString(), "");
|
||
|
}
|
||
|
else {
|
||
|
builder_util_1.log.warn(`Remove the ${identitynumber}`);
|
||
|
result = options.identityName.replace(identitynumber.toString(), "");
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
result = options.identityName || appInfo.name;
|
||
|
}
|
||
|
if (result.length < 1 || result.length > 64) {
|
||
|
const message = `Appx Application.Id with a value between 1 and 64 characters in length`;
|
||
|
throw new builder_util_1.InvalidConfigurationError(message);
|
||
|
}
|
||
|
else if (!validCharactersRegex.test(result)) {
|
||
|
const message = `AppX Application.Id can not be consists of alpha-numeric and period"`;
|
||
|
throw new builder_util_1.InvalidConfigurationError(message);
|
||
|
}
|
||
|
else if (restrictedApplicationIdValues.includes(result.toUpperCase())) {
|
||
|
const message = `AppX identityName.Id can not include restricted values: ${JSON.stringify(restrictedApplicationIdValues)}`;
|
||
|
throw new builder_util_1.InvalidConfigurationError(message);
|
||
|
}
|
||
|
else if (result == null && options.applicationId == null) {
|
||
|
const message = `Please set appx.applicationId (or correct appx.identityName or name)`;
|
||
|
throw new builder_util_1.InvalidConfigurationError(message);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
case "identityName": {
|
||
|
const result = options.identityName || appInfo.name;
|
||
|
const validCharactersRegex = /^[a-zA-Z0-9.-]+$/;
|
||
|
if (result.length < 3 || result.length > 50) {
|
||
|
const message = `Appx identityName.Id with a value between 3 and 50 characters in length`;
|
||
|
throw new builder_util_1.InvalidConfigurationError(message);
|
||
|
}
|
||
|
else if (!validCharactersRegex.test(result)) {
|
||
|
const message = `AppX identityName.Id cat be consists of alpha-numeric, period, and dash characters"`;
|
||
|
throw new builder_util_1.InvalidConfigurationError(message);
|
||
|
}
|
||
|
else if (restrictedApplicationIdValues.includes(result.toUpperCase())) {
|
||
|
const message = `AppX identityName.Id can not be some values`;
|
||
|
throw new builder_util_1.InvalidConfigurationError(message);
|
||
|
}
|
||
|
else if (result == null && options.identityName == null) {
|
||
|
const message = `Please set appx.identityName or name`;
|
||
|
throw new builder_util_1.InvalidConfigurationError(message);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
case "executable":
|
||
|
return executable;
|
||
|
case "displayName":
|
||
|
return displayName;
|
||
|
case "description":
|
||
|
return appInfo.description || appInfo.productName;
|
||
|
case "backgroundColor":
|
||
|
return options.backgroundColor || "#464646";
|
||
|
case "logo":
|
||
|
return "assets\\StoreLogo.png";
|
||
|
case "square150x150Logo":
|
||
|
return "assets\\Square150x150Logo.png";
|
||
|
case "square44x44Logo":
|
||
|
return "assets\\Square44x44Logo.png";
|
||
|
case "lockScreen":
|
||
|
return lockScreenTag(userAssets);
|
||
|
case "defaultTile":
|
||
|
return defaultTileTag(userAssets, options.showNameOnTiles || false);
|
||
|
case "splashScreen":
|
||
|
return splashScreenTag(userAssets);
|
||
|
case "arch":
|
||
|
return arch === builder_util_1.Arch.ia32 ? "x86" : arch === builder_util_1.Arch.arm64 ? "arm64" : "x64";
|
||
|
case "resourceLanguages":
|
||
|
return resourceLanguageTag((0, builder_util_1.asArray)(options.languages));
|
||
|
case "extensions":
|
||
|
return extensions;
|
||
|
case "minVersion":
|
||
|
return options.minVersion || archSpecificMinVersion;
|
||
|
case "maxVersionTested":
|
||
|
return options.maxVersionTested || options.minVersion || archSpecificMinVersion;
|
||
|
default:
|
||
|
throw new Error(`Macro ${p1} is not defined`);
|
||
|
}
|
||
|
});
|
||
|
await (0, fs_extra_1.writeFile)(outFile, manifest);
|
||
|
}
|
||
|
async getExtensions(executable, displayName) {
|
||
|
const uriSchemes = (0, builder_util_1.asArray)(this.packager.config.protocols).concat((0, builder_util_1.asArray)(this.packager.platformSpecificBuildOptions.protocols));
|
||
|
const fileAssociations = (0, builder_util_1.asArray)(this.packager.config.fileAssociations).concat((0, builder_util_1.asArray)(this.packager.platformSpecificBuildOptions.fileAssociations));
|
||
|
let isAddAutoLaunchExtension = this.options.addAutoLaunchExtension;
|
||
|
if (isAddAutoLaunchExtension === undefined) {
|
||
|
const deps = this.packager.info.metadata.dependencies;
|
||
|
isAddAutoLaunchExtension = deps != null && deps["electron-winstore-auto-launch"] != null;
|
||
|
}
|
||
|
if (!isAddAutoLaunchExtension && uriSchemes.length === 0 && fileAssociations.length === 0 && this.options.customExtensionsPath === undefined) {
|
||
|
return "";
|
||
|
}
|
||
|
let extensions = "<Extensions>";
|
||
|
if (isAddAutoLaunchExtension) {
|
||
|
extensions += `
|
||
|
<desktop:Extension Category="windows.startupTask" Executable="${executable}" EntryPoint="Windows.FullTrustApplication">
|
||
|
<desktop:StartupTask TaskId="SlackStartup" Enabled="true" DisplayName="${displayName}" />
|
||
|
</desktop:Extension>`;
|
||
|
}
|
||
|
for (const protocol of uriSchemes) {
|
||
|
for (const scheme of (0, builder_util_1.asArray)(protocol.schemes)) {
|
||
|
extensions += `
|
||
|
<uap:Extension Category="windows.protocol">
|
||
|
<uap:Protocol Name="${scheme}">
|
||
|
<uap:DisplayName>${protocol.name}</uap:DisplayName>
|
||
|
</uap:Protocol>
|
||
|
</uap:Extension>`;
|
||
|
}
|
||
|
}
|
||
|
for (const fileAssociation of fileAssociations) {
|
||
|
for (const ext of (0, builder_util_1.asArray)(fileAssociation.ext)) {
|
||
|
extensions += `
|
||
|
<uap:Extension Category="windows.fileTypeAssociation">
|
||
|
<uap:FileTypeAssociation Name="${ext}">
|
||
|
<uap:SupportedFileTypes>
|
||
|
<uap:FileType>.${ext}</uap:FileType>
|
||
|
</uap:SupportedFileTypes>
|
||
|
</uap:FileTypeAssociation>
|
||
|
</uap:Extension>`;
|
||
|
}
|
||
|
}
|
||
|
if (this.options.customExtensionsPath !== undefined) {
|
||
|
const extensionsPath = path.resolve(this.packager.info.appDir, this.options.customExtensionsPath);
|
||
|
extensions += await (0, fs_extra_1.readFile)(extensionsPath, "utf8");
|
||
|
}
|
||
|
extensions += "</Extensions>";
|
||
|
return extensions;
|
||
|
}
|
||
|
}
|
||
|
exports.default = AppXTarget;
|
||
|
// get the resource - language tag, see https://docs.microsoft.com/en-us/windows/uwp/globalizing/manage-language-and-region#specify-the-supported-languages-in-the-apps-manifest
|
||
|
function resourceLanguageTag(userLanguages) {
|
||
|
if (userLanguages == null || userLanguages.length === 0) {
|
||
|
userLanguages = [DEFAULT_RESOURCE_LANG];
|
||
|
}
|
||
|
return userLanguages.map(it => `<Resource Language="${it.replace(/_/g, "-")}" />`).join("\n");
|
||
|
}
|
||
|
function lockScreenTag(userAssets) {
|
||
|
if (isDefaultAssetIncluded(userAssets, "BadgeLogo.png")) {
|
||
|
return '<uap:LockScreen Notification="badgeAndTileText" BadgeLogo="assets\\BadgeLogo.png" />';
|
||
|
}
|
||
|
else {
|
||
|
return "";
|
||
|
}
|
||
|
}
|
||
|
function defaultTileTag(userAssets, showNameOnTiles) {
|
||
|
const defaultTiles = ["<uap:DefaultTile", 'Wide310x150Logo="assets\\Wide310x150Logo.png"'];
|
||
|
if (isDefaultAssetIncluded(userAssets, "LargeTile.png")) {
|
||
|
defaultTiles.push('Square310x310Logo="assets\\LargeTile.png"');
|
||
|
}
|
||
|
if (isDefaultAssetIncluded(userAssets, "SmallTile.png")) {
|
||
|
defaultTiles.push('Square71x71Logo="assets\\SmallTile.png"');
|
||
|
}
|
||
|
if (showNameOnTiles) {
|
||
|
defaultTiles.push(">");
|
||
|
defaultTiles.push("<uap:ShowNameOnTiles>");
|
||
|
defaultTiles.push("<uap:ShowOn", 'Tile="wide310x150Logo"', "/>");
|
||
|
defaultTiles.push("<uap:ShowOn", 'Tile="square150x150Logo"', "/>");
|
||
|
defaultTiles.push("</uap:ShowNameOnTiles>");
|
||
|
defaultTiles.push("</uap:DefaultTile>");
|
||
|
}
|
||
|
else {
|
||
|
defaultTiles.push("/>");
|
||
|
}
|
||
|
return defaultTiles.join(" ");
|
||
|
}
|
||
|
function splashScreenTag(userAssets) {
|
||
|
if (isDefaultAssetIncluded(userAssets, "SplashScreen.png")) {
|
||
|
return '<uap:SplashScreen Image="assets\\SplashScreen.png" />';
|
||
|
}
|
||
|
else {
|
||
|
return "";
|
||
|
}
|
||
|
}
|
||
|
function isDefaultAssetIncluded(userAssets, defaultAsset) {
|
||
|
const defaultAssetName = defaultAsset.substring(0, defaultAsset.indexOf("."));
|
||
|
return userAssets.some(it => it.includes(defaultAssetName));
|
||
|
}
|
||
|
function isScaledAssetsProvided(userAssets) {
|
||
|
return userAssets.some(it => it.includes(".scale-") || it.includes(".targetsize-"));
|
||
|
}
|
||
|
//# sourceMappingURL=AppxTarget.js.map
|