1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const bluebird_lst_1 = require("bluebird-lst");
|
4 | const builder_util_1 = require("builder-util");
|
5 | const fs_1 = require("builder-util/out/fs");
|
6 | const fs_extra_1 = require("fs-extra");
|
7 | const path = require("path");
|
8 | const windowsCodeSign_1 = require("../codeSign/windowsCodeSign");
|
9 | const core_1 = require("../core");
|
10 | const pathManager_1 = require("../util/pathManager");
|
11 | const targetUtil_1 = require("./targetUtil");
|
12 | const APPX_ASSETS_DIR_NAME = "appx";
|
13 | const vendorAssetsForDefaultAssets = {
|
14 | "StoreLogo.png": "SampleAppx.50x50.png",
|
15 | "Square150x150Logo.png": "SampleAppx.150x150.png",
|
16 | "Square44x44Logo.png": "SampleAppx.44x44.png",
|
17 | "Wide310x150Logo.png": "SampleAppx.310x150.png",
|
18 | };
|
19 | const DEFAULT_RESOURCE_LANG = "en-US";
|
20 | class AppXTarget extends core_1.Target {
|
21 | constructor(packager, outDir) {
|
22 | super("appx");
|
23 | this.packager = packager;
|
24 | this.outDir = outDir;
|
25 | this.options = (0, builder_util_1.deepAssign)({}, this.packager.platformSpecificBuildOptions, this.packager.config.appx);
|
26 | if (process.platform !== "darwin" && (process.platform !== "win32" || (0, windowsCodeSign_1.isOldWin6)())) {
|
27 | throw new Error("AppX is supported only on Windows 10 or Windows Server 2012 R2 (version number 6.3+)");
|
28 | }
|
29 | }
|
30 |
|
31 | async build(appOutDir, arch) {
|
32 | const packager = this.packager;
|
33 | const artifactName = packager.expandArtifactBeautyNamePattern(this.options, "appx", arch);
|
34 | const artifactPath = path.join(this.outDir, artifactName);
|
35 | await packager.info.callArtifactBuildStarted({
|
36 | targetPresentableName: "AppX",
|
37 | file: artifactPath,
|
38 | arch,
|
39 | });
|
40 | const vendorPath = await (0, windowsCodeSign_1.getSignVendorPath)();
|
41 | const vm = await packager.vm.value;
|
42 | const stageDir = await (0, targetUtil_1.createStageDir)(this, packager, arch);
|
43 | const mappingFile = stageDir.getTempFile("mapping.txt");
|
44 | const makeAppXArgs = ["pack", "/o" , "/f", vm.toVmFile(mappingFile), "/p", vm.toVmFile(artifactPath)];
|
45 | if (packager.compression === "store") {
|
46 | makeAppXArgs.push("/nc");
|
47 | }
|
48 | const mappingList = [];
|
49 | mappingList.push(await bluebird_lst_1.default.map((0, fs_1.walk)(appOutDir), file => {
|
50 | let appxPath = file.substring(appOutDir.length + 1);
|
51 | if (path.sep !== "\\") {
|
52 | appxPath = appxPath.replace(/\//g, "\\");
|
53 | }
|
54 | return `"${vm.toVmFile(file)}" "app\\${appxPath}"`;
|
55 | }));
|
56 | const userAssetDir = await this.packager.getResource(undefined, APPX_ASSETS_DIR_NAME);
|
57 | const assetInfo = await AppXTarget.computeUserAssets(vm, vendorPath, userAssetDir);
|
58 | const userAssets = assetInfo.userAssets;
|
59 | const manifestFile = stageDir.getTempFile("AppxManifest.xml");
|
60 | await this.writeManifest(manifestFile, arch, await this.computePublisherName(), userAssets);
|
61 | await packager.info.callAppxManifestCreated(manifestFile);
|
62 | mappingList.push(assetInfo.mappings);
|
63 | mappingList.push([`"${vm.toVmFile(manifestFile)}" "AppxManifest.xml"`]);
|
64 | const signToolArch = arch === builder_util_1.Arch.arm64 ? "x64" : builder_util_1.Arch[arch];
|
65 | if (isScaledAssetsProvided(userAssets)) {
|
66 | const outFile = vm.toVmFile(stageDir.getTempFile("resources.pri"));
|
67 | const makePriPath = vm.toVmFile(path.join(vendorPath, "windows-10", signToolArch, "makepri.exe"));
|
68 | const assetRoot = stageDir.getTempFile("appx/assets");
|
69 | await (0, fs_extra_1.emptyDir)(assetRoot);
|
70 | await bluebird_lst_1.default.map(assetInfo.allAssets, it => (0, fs_1.copyOrLinkFile)(it, path.join(assetRoot, path.basename(it))));
|
71 | await vm.exec(makePriPath, [
|
72 | "new",
|
73 | "/Overwrite",
|
74 | "/Manifest",
|
75 | vm.toVmFile(manifestFile),
|
76 | "/ProjectRoot",
|
77 | vm.toVmFile(path.dirname(assetRoot)),
|
78 | "/ConfigXml",
|
79 | vm.toVmFile(path.join((0, pathManager_1.getTemplatePath)("appx"), "priconfig.xml")),
|
80 | "/OutputFile",
|
81 | outFile,
|
82 | ]);
|
83 |
|
84 | for (const resourceFile of (await (0, fs_extra_1.readdir)(stageDir.dir)).filter(it => it.startsWith("resources.")).sort()) {
|
85 | mappingList.push([`"${vm.toVmFile(stageDir.getTempFile(resourceFile))}" "${resourceFile}"`]);
|
86 | }
|
87 | makeAppXArgs.push("/l");
|
88 | }
|
89 | let mapping = "[Files]";
|
90 | for (const list of mappingList) {
|
91 | mapping += "\r\n" + list.join("\r\n");
|
92 | }
|
93 | await (0, fs_extra_1.writeFile)(mappingFile, mapping);
|
94 | packager.debugLogger.add("appx.mapping", mapping);
|
95 | if (this.options.makeappxArgs != null) {
|
96 | makeAppXArgs.push(...this.options.makeappxArgs);
|
97 | }
|
98 | await vm.exec(vm.toVmFile(path.join(vendorPath, "windows-10", signToolArch, "makeappx.exe")), makeAppXArgs);
|
99 | await packager.sign(artifactPath);
|
100 | await stageDir.cleanup();
|
101 | await packager.info.callArtifactBuildCompleted({
|
102 | file: artifactPath,
|
103 | packager,
|
104 | arch,
|
105 | safeArtifactName: packager.computeSafeArtifactName(artifactName, "appx"),
|
106 | target: this,
|
107 | isWriteUpdateInfo: this.options.electronUpdaterAware,
|
108 | });
|
109 | }
|
110 | static async computeUserAssets(vm, vendorPath, userAssetDir) {
|
111 | const mappings = [];
|
112 | let userAssets;
|
113 | const allAssets = [];
|
114 | if (userAssetDir == null) {
|
115 | userAssets = [];
|
116 | }
|
117 | else {
|
118 | userAssets = (await (0, fs_extra_1.readdir)(userAssetDir)).filter(it => !it.startsWith(".") && !it.endsWith(".db") && it.includes("."));
|
119 | for (const name of userAssets) {
|
120 | mappings.push(`"${vm.toVmFile(userAssetDir)}${vm.pathSep}${name}" "assets\\${name}"`);
|
121 | allAssets.push(path.join(userAssetDir, name));
|
122 | }
|
123 | }
|
124 | for (const defaultAsset of Object.keys(vendorAssetsForDefaultAssets)) {
|
125 | if (userAssets.length === 0 || !isDefaultAssetIncluded(userAssets, defaultAsset)) {
|
126 | const file = path.join(vendorPath, "appxAssets", vendorAssetsForDefaultAssets[defaultAsset]);
|
127 | mappings.push(`"${vm.toVmFile(file)}" "assets\\${defaultAsset}"`);
|
128 | allAssets.push(file);
|
129 | }
|
130 | }
|
131 |
|
132 | return { userAssets, mappings, allAssets };
|
133 | }
|
134 |
|
135 | async computePublisherName() {
|
136 | if ((await this.packager.cscInfo.value) == null) {
|
137 | builder_util_1.log.info({ reason: "Windows Store only build" }, "AppX is not signed");
|
138 | return this.options.publisher || "CN=ms";
|
139 | }
|
140 | const certInfo = await this.packager.lazyCertInfo.value;
|
141 | const publisher = this.options.publisher || (certInfo == null ? null : certInfo.bloodyMicrosoftSubjectDn);
|
142 | if (publisher == null) {
|
143 | throw new Error("Internal error: cannot compute subject using certificate info");
|
144 | }
|
145 | return publisher;
|
146 | }
|
147 | async writeManifest(outFile, arch, publisher, userAssets) {
|
148 | const appInfo = this.packager.appInfo;
|
149 | const options = this.options;
|
150 | const executable = `app\\${appInfo.productFilename}.exe`;
|
151 | const displayName = options.displayName || appInfo.productName;
|
152 | const extensions = await this.getExtensions(executable, displayName);
|
153 | const archSpecificMinVersion = arch === builder_util_1.Arch.arm64 ? "10.0.16299.0" : "10.0.14316.0";
|
154 | const manifest = (await (0, fs_extra_1.readFile)(path.join((0, pathManager_1.getTemplatePath)("appx"), "appxmanifest.xml"), "utf8")).replace(/\${([a-zA-Z0-9]+)}/g, (match, p1) => {
|
155 | switch (p1) {
|
156 | case "publisher":
|
157 | return publisher;
|
158 | case "publisherDisplayName": {
|
159 | const name = options.publisherDisplayName || appInfo.companyName;
|
160 | if (name == null) {
|
161 | throw new builder_util_1.InvalidConfigurationError(`Please specify "author" in the application package.json — it is required because "appx.publisherDisplayName" is not set.`);
|
162 | }
|
163 | return name;
|
164 | }
|
165 | case "version":
|
166 | return appInfo.getVersionInWeirdWindowsForm(options.setBuildNumber === true);
|
167 | case "applicationId": {
|
168 | const result = options.applicationId || options.identityName || appInfo.name;
|
169 | if (!isNaN(parseInt(result[0], 10))) {
|
170 | let message = `AppX Application.Id can’t start with numbers: "${result}"`;
|
171 | if (options.applicationId == null) {
|
172 | message += `\nPlease set appx.applicationId (or correct appx.identityName or name)`;
|
173 | }
|
174 | throw new builder_util_1.InvalidConfigurationError(message);
|
175 | }
|
176 | return result;
|
177 | }
|
178 | case "identityName":
|
179 | return options.identityName || appInfo.name;
|
180 | case "executable":
|
181 | return executable;
|
182 | case "displayName":
|
183 | return displayName;
|
184 | case "description":
|
185 | return appInfo.description || appInfo.productName;
|
186 | case "backgroundColor":
|
187 | return options.backgroundColor || "#464646";
|
188 | case "logo":
|
189 | return "assets\\StoreLogo.png";
|
190 | case "square150x150Logo":
|
191 | return "assets\\Square150x150Logo.png";
|
192 | case "square44x44Logo":
|
193 | return "assets\\Square44x44Logo.png";
|
194 | case "lockScreen":
|
195 | return lockScreenTag(userAssets);
|
196 | case "defaultTile":
|
197 | return defaultTileTag(userAssets, options.showNameOnTiles || false);
|
198 | case "splashScreen":
|
199 | return splashScreenTag(userAssets);
|
200 | case "arch":
|
201 | return arch === builder_util_1.Arch.ia32 ? "x86" : arch === builder_util_1.Arch.arm64 ? "arm64" : "x64";
|
202 | case "resourceLanguages":
|
203 | return resourceLanguageTag((0, builder_util_1.asArray)(options.languages));
|
204 | case "extensions":
|
205 | return extensions;
|
206 | case "minVersion":
|
207 | return options.minVersion || archSpecificMinVersion;
|
208 | case "maxVersionTested":
|
209 | return options.maxVersionTested || options.minVersion || archSpecificMinVersion;
|
210 | default:
|
211 | throw new Error(`Macro ${p1} is not defined`);
|
212 | }
|
213 | });
|
214 | await (0, fs_extra_1.writeFile)(outFile, manifest);
|
215 | }
|
216 | async getExtensions(executable, displayName) {
|
217 | const uriSchemes = (0, builder_util_1.asArray)(this.packager.config.protocols).concat((0, builder_util_1.asArray)(this.packager.platformSpecificBuildOptions.protocols));
|
218 | const fileAssociations = (0, builder_util_1.asArray)(this.packager.config.fileAssociations).concat((0, builder_util_1.asArray)(this.packager.platformSpecificBuildOptions.fileAssociations));
|
219 | let isAddAutoLaunchExtension = this.options.addAutoLaunchExtension;
|
220 | if (isAddAutoLaunchExtension === undefined) {
|
221 | const deps = this.packager.info.metadata.dependencies;
|
222 | isAddAutoLaunchExtension = deps != null && deps["electron-winstore-auto-launch"] != null;
|
223 | }
|
224 | if (!isAddAutoLaunchExtension && uriSchemes.length === 0 && fileAssociations.length === 0 && this.options.customExtensionsPath === undefined) {
|
225 | return "";
|
226 | }
|
227 | let extensions = "<Extensions>";
|
228 | if (isAddAutoLaunchExtension) {
|
229 | extensions += `
|
230 | <desktop:Extension Category="windows.startupTask" Executable="${executable}" EntryPoint="Windows.FullTrustApplication">
|
231 | <desktop:StartupTask TaskId="SlackStartup" Enabled="true" DisplayName="${displayName}" />
|
232 | </desktop:Extension>`;
|
233 | }
|
234 | for (const protocol of uriSchemes) {
|
235 | for (const scheme of (0, builder_util_1.asArray)(protocol.schemes)) {
|
236 | extensions += `
|
237 | <uap:Extension Category="windows.protocol">
|
238 | <uap:Protocol Name="${scheme}">
|
239 | <uap:DisplayName>${protocol.name}</uap:DisplayName>
|
240 | </uap:Protocol>
|
241 | </uap:Extension>`;
|
242 | }
|
243 | }
|
244 | for (const fileAssociation of fileAssociations) {
|
245 | for (const ext of (0, builder_util_1.asArray)(fileAssociation.ext)) {
|
246 | extensions += `
|
247 | <uap:Extension Category="windows.fileTypeAssociation">
|
248 | <uap:FileTypeAssociation Name="${ext}">
|
249 | <uap:SupportedFileTypes>
|
250 | <uap:FileType>.${ext}</uap:FileType>
|
251 | </uap:SupportedFileTypes>
|
252 | </uap:FileTypeAssociation>
|
253 | </uap:Extension>`;
|
254 | }
|
255 | }
|
256 | if (this.options.customExtensionsPath !== undefined) {
|
257 | const extensionsPath = path.resolve(this.packager.info.appDir, this.options.customExtensionsPath);
|
258 | extensions += await (0, fs_extra_1.readFile)(extensionsPath, "utf8");
|
259 | }
|
260 | extensions += "</Extensions>";
|
261 | return extensions;
|
262 | }
|
263 | }
|
264 | exports.default = AppXTarget;
|
265 |
|
266 | function resourceLanguageTag(userLanguages) {
|
267 | if (userLanguages == null || userLanguages.length === 0) {
|
268 | userLanguages = [DEFAULT_RESOURCE_LANG];
|
269 | }
|
270 | return userLanguages.map(it => `<Resource Language="${it.replace(/_/g, "-")}" />`).join("\n");
|
271 | }
|
272 | function lockScreenTag(userAssets) {
|
273 | if (isDefaultAssetIncluded(userAssets, "BadgeLogo.png")) {
|
274 | return '<uap:LockScreen Notification="badgeAndTileText" BadgeLogo="assets\\BadgeLogo.png" />';
|
275 | }
|
276 | else {
|
277 | return "";
|
278 | }
|
279 | }
|
280 | function defaultTileTag(userAssets, showNameOnTiles) {
|
281 | const defaultTiles = ["<uap:DefaultTile", 'Wide310x150Logo="assets\\Wide310x150Logo.png"'];
|
282 | if (isDefaultAssetIncluded(userAssets, "LargeTile.png")) {
|
283 | defaultTiles.push('Square310x310Logo="assets\\LargeTile.png"');
|
284 | }
|
285 | if (isDefaultAssetIncluded(userAssets, "SmallTile.png")) {
|
286 | defaultTiles.push('Square71x71Logo="assets\\SmallTile.png"');
|
287 | }
|
288 | if (showNameOnTiles) {
|
289 | defaultTiles.push(">");
|
290 | defaultTiles.push("<uap:ShowNameOnTiles>");
|
291 | defaultTiles.push("<uap:ShowOn", 'Tile="wide310x150Logo"', "/>");
|
292 | defaultTiles.push("<uap:ShowOn", 'Tile="square150x150Logo"', "/>");
|
293 | defaultTiles.push("</uap:ShowNameOnTiles>");
|
294 | defaultTiles.push("</uap:DefaultTile>");
|
295 | }
|
296 | else {
|
297 | defaultTiles.push("/>");
|
298 | }
|
299 | return defaultTiles.join(" ");
|
300 | }
|
301 | function splashScreenTag(userAssets) {
|
302 | if (isDefaultAssetIncluded(userAssets, "SplashScreen.png")) {
|
303 | return '<uap:SplashScreen Image="assets\\SplashScreen.png" />';
|
304 | }
|
305 | else {
|
306 | return "";
|
307 | }
|
308 | }
|
309 | function isDefaultAssetIncluded(userAssets, defaultAsset) {
|
310 | const defaultAssetName = defaultAsset.substring(0, defaultAsset.indexOf("."));
|
311 | return userAssets.some(it => it.includes(defaultAssetName));
|
312 | }
|
313 | function isScaledAssetsProvided(userAssets) {
|
314 | return userAssets.some(it => it.includes(".scale-") || it.includes(".targetsize-"));
|
315 | }
|
316 |
|
\ | No newline at end of file |