@react-native-windows/cli 0.73.2 → 0.74.0-preview.1
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/lib-commonjs/commands/autolinkWindows/autolinkWindows.d.ts +87 -87
- package/lib-commonjs/commands/autolinkWindows/autolinkWindows.js +654 -654
- package/lib-commonjs/commands/autolinkWindows/autolinkWindowsOptions.d.ts +14 -14
- package/lib-commonjs/commands/autolinkWindows/autolinkWindowsOptions.js +32 -32
- package/lib-commonjs/commands/codegenWindows/codegenWindows.d.ts +27 -27
- package/lib-commonjs/commands/codegenWindows/codegenWindows.js +205 -205
- package/lib-commonjs/commands/codegenWindows/codegenWindowsOptions.d.ts +12 -12
- package/lib-commonjs/commands/codegenWindows/codegenWindowsOptions.js +22 -22
- package/lib-commonjs/commands/config/configUtils.d.ts +123 -123
- package/lib-commonjs/commands/config/configUtils.js +380 -380
- package/lib-commonjs/commands/config/dependencyConfig.d.ts +37 -37
- package/lib-commonjs/commands/config/dependencyConfig.js +227 -227
- package/lib-commonjs/commands/config/projectConfig.d.ts +27 -27
- package/lib-commonjs/commands/config/projectConfig.js +180 -180
- package/lib-commonjs/commands/healthCheck/healthCheckList.d.ts +6 -6
- package/lib-commonjs/commands/healthCheck/healthCheckList.js +20 -20
- package/lib-commonjs/commands/healthCheck/healthCheckList.js.map +1 -1
- package/lib-commonjs/commands/healthCheck/healthChecks.d.ts +7 -7
- package/lib-commonjs/commands/healthCheck/healthChecks.js +123 -123
- package/lib-commonjs/commands/initWindows/initWindows.d.ts +46 -46
- package/lib-commonjs/commands/initWindows/initWindows.js +243 -243
- package/lib-commonjs/commands/initWindows/initWindowsOptions.d.ts +15 -15
- package/lib-commonjs/commands/initWindows/initWindowsOptions.js +37 -37
- package/lib-commonjs/commands/runWindows/runWindows.d.ts +10 -10
- package/lib-commonjs/commands/runWindows/runWindows.js +321 -321
- package/lib-commonjs/commands/runWindows/runWindowsOptions.d.ts +56 -56
- package/lib-commonjs/commands/runWindows/runWindowsOptions.js +132 -132
- package/lib-commonjs/e2etest/autolink.test.d.ts +6 -6
- package/lib-commonjs/e2etest/autolink.test.js +366 -366
- package/lib-commonjs/e2etest/dependencyConfig.test.d.ts +6 -6
- package/lib-commonjs/e2etest/dependencyConfig.test.js +129 -129
- package/lib-commonjs/e2etest/healthChecks.test.d.ts +6 -6
- package/lib-commonjs/e2etest/healthChecks.test.js +30 -30
- package/lib-commonjs/e2etest/initWindows.test.d.ts +6 -6
- package/lib-commonjs/e2etest/initWindows.test.js +42 -42
- package/lib-commonjs/e2etest/projectConfig.test.d.ts +6 -6
- package/lib-commonjs/e2etest/projectConfig.test.js +110 -110
- package/lib-commonjs/e2etest/projectConfig.utils.d.ts +8 -8
- package/lib-commonjs/e2etest/projectConfig.utils.js +76 -76
- package/lib-commonjs/e2etest/runWindows.test.d.ts +6 -6
- package/lib-commonjs/e2etest/runWindows.test.js +60 -60
- package/lib-commonjs/e2etest/typesUpToDate.test.d.ts +6 -0
- package/lib-commonjs/e2etest/typesUpToDate.test.js +21 -0
- package/lib-commonjs/e2etest/typesUpToDate.test.js.map +1 -0
- package/lib-commonjs/generator-common/index.d.ts +39 -39
- package/lib-commonjs/generator-common/index.js +242 -242
- package/lib-commonjs/generator-windows/index.d.ts +10 -10
- package/lib-commonjs/generator-windows/index.js +316 -316
- package/lib-commonjs/index.d.ts +50 -50
- package/lib-commonjs/index.js +77 -77
- package/lib-commonjs/utils/build.d.ts +12 -12
- package/lib-commonjs/utils/build.js +84 -84
- package/lib-commonjs/utils/checkRequirements.d.ts +6 -6
- package/lib-commonjs/utils/checkRequirements.js +69 -69
- package/lib-commonjs/utils/commandWithProgress.d.ts +21 -21
- package/lib-commonjs/utils/commandWithProgress.js +149 -149
- package/lib-commonjs/utils/deploy.d.ts +12 -12
- package/lib-commonjs/utils/deploy.js +353 -353
- package/lib-commonjs/utils/info.d.ts +6 -6
- package/lib-commonjs/utils/info.js +28 -28
- package/lib-commonjs/utils/msbuildtools.d.ts +28 -28
- package/lib-commonjs/utils/msbuildtools.js +273 -273
- package/lib-commonjs/utils/msbuildtools.js.map +1 -1
- package/lib-commonjs/utils/pathHelpers.d.ts +9 -9
- package/lib-commonjs/utils/pathHelpers.js +36 -36
- package/lib-commonjs/utils/telemetryHelpers.d.ts +29 -29
- package/lib-commonjs/utils/telemetryHelpers.js +120 -120
- package/lib-commonjs/utils/version.d.ts +19 -19
- package/lib-commonjs/utils/version.js +109 -109
- package/lib-commonjs/utils/vsInstalls.d.ts +34 -34
- package/lib-commonjs/utils/vsInstalls.js +99 -99
- package/lib-commonjs/utils/vstools.d.ts +16 -16
- package/lib-commonjs/utils/vstools.js +189 -189
- package/lib-commonjs/utils/winappdeploytool.d.ts +24 -24
- package/lib-commonjs/utils/winappdeploytool.js +108 -108
- package/package.json +16 -16
|
@@ -1,661 +1,661 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) Microsoft Corporation.
|
|
4
|
-
* Licensed under the MIT License.
|
|
5
|
-
* @format
|
|
6
|
-
*/
|
|
7
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
-
if (k2 === undefined) k2 = k;
|
|
9
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
-
}
|
|
13
|
-
Object.defineProperty(o, k2, desc);
|
|
14
|
-
}) : (function(o, m, k, k2) {
|
|
15
|
-
if (k2 === undefined) k2 = k;
|
|
16
|
-
o[k2] = m[k];
|
|
17
|
-
}));
|
|
18
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
-
}) : function(o, v) {
|
|
21
|
-
o["default"] = v;
|
|
22
|
-
});
|
|
23
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
24
|
-
if (mod && mod.__esModule) return mod;
|
|
25
|
-
var result = {};
|
|
26
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
27
|
-
__setModuleDefault(result, mod);
|
|
28
|
-
return result;
|
|
29
|
-
};
|
|
30
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
31
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
32
|
-
};
|
|
33
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
-
exports.autolinkCommand = exports.autolinkWindowsInternal = exports.AutoLinkWindows = void 0;
|
|
35
|
-
// Types in this file are inaccurate compared to usage in terms of falsiness.
|
|
36
|
-
// We should try to rewrite some of this to do automated schema validation to
|
|
37
|
-
// guarantee correct types
|
|
38
|
-
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
39
|
-
const fs_1 = __importDefault(require("@react-native-windows/fs"));
|
|
40
|
-
const path_1 = __importDefault(require("path"));
|
|
41
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
-
const perf_hooks_1 = require("perf_hooks");
|
|
43
|
-
const xmldom_1 = require("@xmldom/xmldom");
|
|
44
|
-
const formatter = require('xml-formatter');
|
|
45
|
-
const telemetry_1 = require("@react-native-windows/telemetry");
|
|
46
|
-
const autolinkWindowsOptions_1 = require("./autolinkWindowsOptions");
|
|
47
|
-
const commandWithProgress_1 = require("../../utils/commandWithProgress");
|
|
48
|
-
const vstools = __importStar(require("../../utils/vstools"));
|
|
49
|
-
const generatorCommon = __importStar(require("../../generator-common"));
|
|
50
|
-
const configUtils = __importStar(require("../config/configUtils"));
|
|
51
|
-
const pathHelpers = __importStar(require("../../utils/pathHelpers"));
|
|
52
|
-
const telemetryHelpers_1 = require("../../utils/telemetryHelpers");
|
|
53
|
-
class AutoLinkWindows {
|
|
54
|
-
areChangesNeeded() {
|
|
55
|
-
return this.changesNecessary;
|
|
56
|
-
}
|
|
57
|
-
getWindowsConfig() {
|
|
58
|
-
return this.windowsAppConfig;
|
|
59
|
-
}
|
|
60
|
-
getSolutionFile() {
|
|
61
|
-
return path_1.default.join(this.getWindowsConfig().folder, this.getWindowsConfig().sourceDir, this.getWindowsConfig().solutionFile);
|
|
62
|
-
}
|
|
63
|
-
constructor(projectConfig, dependenciesConfig, options) {
|
|
64
|
-
this.projectConfig = projectConfig;
|
|
65
|
-
this.dependenciesConfig = dependenciesConfig;
|
|
66
|
-
this.options = options;
|
|
67
|
-
this.changesNecessary = false;
|
|
68
|
-
if (!('windows' in this.projectConfig) ||
|
|
69
|
-
this.projectConfig.windows === null) {
|
|
70
|
-
throw new telemetry_1.CodedError('NoWindowsConfig', 'Windows auto-link only supported on Windows app projects');
|
|
71
|
-
}
|
|
72
|
-
this.windowsAppConfig = projectConfig.windows;
|
|
73
|
-
}
|
|
74
|
-
async run(spinner) {
|
|
75
|
-
const verbose = this.options.logging;
|
|
76
|
-
verboseMessage('', verbose);
|
|
77
|
-
verboseMessage('Parsing project...', verbose);
|
|
78
|
-
const rnwRoot = resolveRnwRoot(this.windowsAppConfig);
|
|
79
|
-
const templateRoot = resolveTemplateRoot(this.windowsAppConfig);
|
|
80
|
-
this.fixUpForSlnOption();
|
|
81
|
-
this.fixUpForProjOption();
|
|
82
|
-
verboseMessage('Found Windows app project, config:', verbose);
|
|
83
|
-
verboseMessage(this.windowsAppConfig, verbose);
|
|
84
|
-
this.validateRequiredAppProperties();
|
|
85
|
-
const solutionFile = this.getSolutionFile();
|
|
86
|
-
const windowsAppProjectConfig = this.windowsAppConfig.project;
|
|
87
|
-
this.validateRequiredProjectProperties();
|
|
88
|
-
const projectFile = this.getProjectFile();
|
|
89
|
-
const projectDir = path_1.default.dirname(projectFile);
|
|
90
|
-
const projectLang = windowsAppProjectConfig.projectLang;
|
|
91
|
-
verboseMessage('Parsing dependencies...', verbose);
|
|
92
|
-
this.changesNecessary =
|
|
93
|
-
(await this.ensureXAMLDialect()) || this.changesNecessary;
|
|
94
|
-
// Generating cs/cpp files for app code consumption
|
|
95
|
-
if (projectLang === 'cs') {
|
|
96
|
-
this.changesNecessary =
|
|
97
|
-
(await this.generateCSAutolinking(templateRoot, projectLang, projectDir)) || this.changesNecessary;
|
|
98
|
-
}
|
|
99
|
-
else if (projectLang === 'cpp') {
|
|
100
|
-
this.changesNecessary =
|
|
101
|
-
(await this.generateCppAutolinking(templateRoot, projectLang, projectDir)) || this.changesNecessary;
|
|
102
|
-
}
|
|
103
|
-
// Generating props for app project consumption
|
|
104
|
-
let propertiesForProps = '';
|
|
105
|
-
let csModuleNames = [];
|
|
106
|
-
if (projectLang === 'cpp') {
|
|
107
|
-
csModuleNames = this.getCSModules();
|
|
108
|
-
if (csModuleNames.length > 0) {
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Microsoft Corporation.
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
* @format
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
24
|
+
if (mod && mod.__esModule) return mod;
|
|
25
|
+
var result = {};
|
|
26
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
27
|
+
__setModuleDefault(result, mod);
|
|
28
|
+
return result;
|
|
29
|
+
};
|
|
30
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
31
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
32
|
+
};
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
exports.autolinkCommand = exports.autolinkWindowsInternal = exports.AutoLinkWindows = void 0;
|
|
35
|
+
// Types in this file are inaccurate compared to usage in terms of falsiness.
|
|
36
|
+
// We should try to rewrite some of this to do automated schema validation to
|
|
37
|
+
// guarantee correct types
|
|
38
|
+
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
39
|
+
const fs_1 = __importDefault(require("@react-native-windows/fs"));
|
|
40
|
+
const path_1 = __importDefault(require("path"));
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const perf_hooks_1 = require("perf_hooks");
|
|
43
|
+
const xmldom_1 = require("@xmldom/xmldom");
|
|
44
|
+
const formatter = require('xml-formatter');
|
|
45
|
+
const telemetry_1 = require("@react-native-windows/telemetry");
|
|
46
|
+
const autolinkWindowsOptions_1 = require("./autolinkWindowsOptions");
|
|
47
|
+
const commandWithProgress_1 = require("../../utils/commandWithProgress");
|
|
48
|
+
const vstools = __importStar(require("../../utils/vstools"));
|
|
49
|
+
const generatorCommon = __importStar(require("../../generator-common"));
|
|
50
|
+
const configUtils = __importStar(require("../config/configUtils"));
|
|
51
|
+
const pathHelpers = __importStar(require("../../utils/pathHelpers"));
|
|
52
|
+
const telemetryHelpers_1 = require("../../utils/telemetryHelpers");
|
|
53
|
+
class AutoLinkWindows {
|
|
54
|
+
areChangesNeeded() {
|
|
55
|
+
return this.changesNecessary;
|
|
56
|
+
}
|
|
57
|
+
getWindowsConfig() {
|
|
58
|
+
return this.windowsAppConfig;
|
|
59
|
+
}
|
|
60
|
+
getSolutionFile() {
|
|
61
|
+
return path_1.default.join(this.getWindowsConfig().folder, this.getWindowsConfig().sourceDir, this.getWindowsConfig().solutionFile);
|
|
62
|
+
}
|
|
63
|
+
constructor(projectConfig, dependenciesConfig, options) {
|
|
64
|
+
this.projectConfig = projectConfig;
|
|
65
|
+
this.dependenciesConfig = dependenciesConfig;
|
|
66
|
+
this.options = options;
|
|
67
|
+
this.changesNecessary = false;
|
|
68
|
+
if (!('windows' in this.projectConfig) ||
|
|
69
|
+
this.projectConfig.windows === null) {
|
|
70
|
+
throw new telemetry_1.CodedError('NoWindowsConfig', 'Windows auto-link only supported on Windows app projects');
|
|
71
|
+
}
|
|
72
|
+
this.windowsAppConfig = projectConfig.windows;
|
|
73
|
+
}
|
|
74
|
+
async run(spinner) {
|
|
75
|
+
const verbose = this.options.logging;
|
|
76
|
+
verboseMessage('', verbose);
|
|
77
|
+
verboseMessage('Parsing project...', verbose);
|
|
78
|
+
const rnwRoot = resolveRnwRoot(this.windowsAppConfig);
|
|
79
|
+
const templateRoot = resolveTemplateRoot(this.windowsAppConfig);
|
|
80
|
+
this.fixUpForSlnOption();
|
|
81
|
+
this.fixUpForProjOption();
|
|
82
|
+
verboseMessage('Found Windows app project, config:', verbose);
|
|
83
|
+
verboseMessage(this.windowsAppConfig, verbose);
|
|
84
|
+
this.validateRequiredAppProperties();
|
|
85
|
+
const solutionFile = this.getSolutionFile();
|
|
86
|
+
const windowsAppProjectConfig = this.windowsAppConfig.project;
|
|
87
|
+
this.validateRequiredProjectProperties();
|
|
88
|
+
const projectFile = this.getProjectFile();
|
|
89
|
+
const projectDir = path_1.default.dirname(projectFile);
|
|
90
|
+
const projectLang = windowsAppProjectConfig.projectLang;
|
|
91
|
+
verboseMessage('Parsing dependencies...', verbose);
|
|
92
|
+
this.changesNecessary =
|
|
93
|
+
(await this.ensureXAMLDialect()) || this.changesNecessary;
|
|
94
|
+
// Generating cs/cpp files for app code consumption
|
|
95
|
+
if (projectLang === 'cs') {
|
|
96
|
+
this.changesNecessary =
|
|
97
|
+
(await this.generateCSAutolinking(templateRoot, projectLang, projectDir)) || this.changesNecessary;
|
|
98
|
+
}
|
|
99
|
+
else if (projectLang === 'cpp') {
|
|
100
|
+
this.changesNecessary =
|
|
101
|
+
(await this.generateCppAutolinking(templateRoot, projectLang, projectDir)) || this.changesNecessary;
|
|
102
|
+
}
|
|
103
|
+
// Generating props for app project consumption
|
|
104
|
+
let propertiesForProps = '';
|
|
105
|
+
let csModuleNames = [];
|
|
106
|
+
if (projectLang === 'cpp') {
|
|
107
|
+
csModuleNames = this.getCSModules();
|
|
108
|
+
if (csModuleNames.length > 0) {
|
|
109
109
|
propertiesForProps += `
|
|
110
110
|
<!-- Set due to dependency on C# module(s): ${csModuleNames.join()} -->
|
|
111
|
-
<ConsumeCSharpModules Condition="'$(ConsumeCSharpModules)'==''">true</ConsumeCSharpModules>`;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
this.changesNecessary =
|
|
115
|
-
(await this.generateAutolinkProps(templateRoot, projectDir, propertiesForProps)) || this.changesNecessary;
|
|
116
|
-
// Generating targets for app project consumption
|
|
117
|
-
this.changesNecessary =
|
|
118
|
-
(await this.generateAutolinkTargets(projectDir, templateRoot)) ||
|
|
119
|
-
this.changesNecessary;
|
|
120
|
-
// Generating project entries for solution
|
|
121
|
-
this.changesNecessary =
|
|
122
|
-
this.updateSolution(rnwRoot, solutionFile) || this.changesNecessary;
|
|
123
|
-
spinner.succeed();
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Handles the --proj command-line option by consuming its value into the windowsAppConfig
|
|
127
|
-
*/
|
|
128
|
-
fixUpForProjOption() {
|
|
129
|
-
if (this.options.proj) {
|
|
130
|
-
const projFile = path_1.default.join(this.windowsAppConfig.folder, this.options.proj);
|
|
131
|
-
const projectContents = configUtils.readProjectFile(projFile);
|
|
132
|
-
this.windowsAppConfig.project = {
|
|
133
|
-
projectFile: path_1.default.relative(path_1.default.join(this.windowsAppConfig.folder, this.windowsAppConfig.sourceDir), projFile),
|
|
134
|
-
projectName: configUtils.getProjectName(projFile, projectContents),
|
|
135
|
-
projectLang: configUtils.getProjectLanguage(projFile),
|
|
136
|
-
projectGuid: configUtils.getProjectGuid(projectContents),
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Handles the --sln command-line option by consuming its value into the windowsAppConfig
|
|
142
|
-
*/
|
|
143
|
-
fixUpForSlnOption() {
|
|
144
|
-
if (this.options.sln) {
|
|
145
|
-
const slnFile = path_1.default.join(this.windowsAppConfig.folder, this.options.sln);
|
|
146
|
-
this.windowsAppConfig.solutionFile = path_1.default.relative(path_1.default.join(this.windowsAppConfig.folder, this.windowsAppConfig.sourceDir), slnFile);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
/** Validates the all of the required app (solution) properties are present and valid */
|
|
150
|
-
validateRequiredAppProperties() {
|
|
151
|
-
const alwaysRequired = [
|
|
152
|
-
'folder',
|
|
153
|
-
'sourceDir',
|
|
154
|
-
'solutionFile',
|
|
155
|
-
'project',
|
|
156
|
-
];
|
|
157
|
-
alwaysRequired.forEach(item => {
|
|
158
|
-
if (!(item in this.windowsAppConfig) ||
|
|
159
|
-
this.windowsAppConfig[item] === null) {
|
|
160
|
-
throw new telemetry_1.CodedError('IncompleteConfig', `${item} is required but not specified by react-native config`, { item: item });
|
|
161
|
-
}
|
|
162
|
-
else if (typeof this.windowsAppConfig[item] === 'string' &&
|
|
163
|
-
this.windowsAppConfig[item].startsWith('Error: ')) {
|
|
164
|
-
throw new telemetry_1.CodedError('InvalidConfig', `${item} invalid. ${this.windowsAppConfig[item]}`, { item: item });
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
/** @return the full path to the project file (.vcxproj or .csproj) */
|
|
169
|
-
getProjectFile() {
|
|
170
|
-
const windowsAppConfig = this.getWindowsConfig();
|
|
171
|
-
return path_1.default.join(windowsAppConfig.folder, windowsAppConfig.sourceDir, windowsAppConfig.project.projectFile);
|
|
172
|
-
}
|
|
173
|
-
/** Validates that all of the required app _project_ properties are present and valid */
|
|
174
|
-
validateRequiredProjectProperties() {
|
|
175
|
-
const windowsAppProjectConfig = this.windowsAppConfig.project;
|
|
176
|
-
const projectRequired = [
|
|
177
|
-
'projectFile',
|
|
178
|
-
'projectName',
|
|
179
|
-
'projectLang',
|
|
180
|
-
'projectGuid',
|
|
181
|
-
];
|
|
182
|
-
projectRequired.forEach(item => {
|
|
183
|
-
if (!(item in windowsAppProjectConfig) ||
|
|
184
|
-
windowsAppProjectConfig[item] === null) {
|
|
185
|
-
throw new telemetry_1.CodedError('IncompleteConfig', `project.${item} is required but not specified by react-native config`, { item: item });
|
|
186
|
-
}
|
|
187
|
-
else if (typeof windowsAppProjectConfig[item] === 'string' &&
|
|
188
|
-
windowsAppProjectConfig[item].startsWith('Error: ')) {
|
|
189
|
-
throw new telemetry_1.CodedError('InvalidConfig', `project.${item} invalid. ${windowsAppProjectConfig[item]}`, { item: item });
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
async generateCppAutolinking(templateRoot, projectLang, projectDir) {
|
|
194
|
-
const { cppPackageProviders, cppIncludes } = this.getCppReplacements();
|
|
195
|
-
const cppFileName = 'AutolinkedNativeModules.g.cpp';
|
|
196
|
-
const srcCppFile = path_1.default.join(templateRoot, `${projectLang}-app`, 'src', cppFileName);
|
|
197
|
-
const destCppFile = path_1.default.join(projectDir, cppFileName);
|
|
198
|
-
verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(destCppFile))}...`, this.options.logging);
|
|
199
|
-
const cppContents = generatorCommon.resolveContents(srcCppFile, {
|
|
200
|
-
useMustache: true,
|
|
201
|
-
autolinkCppIncludes: cppIncludes,
|
|
202
|
-
autolinkCppPackageProviders: cppPackageProviders,
|
|
203
|
-
});
|
|
204
|
-
return await this.updateFile(destCppFile, cppContents);
|
|
205
|
-
}
|
|
206
|
-
getCppReplacements() {
|
|
207
|
-
let cppIncludes = '';
|
|
208
|
-
let cppPackageProviders = '';
|
|
209
|
-
const windowsDependencies = this.getWindowsDependencies();
|
|
210
|
-
for (const dependencyName of Object.keys(windowsDependencies)) {
|
|
211
|
-
windowsDependencies[dependencyName].projects.forEach(project => {
|
|
212
|
-
if (project.directDependency) {
|
|
213
|
-
cppIncludes += `\n\n// Includes from ${dependencyName}`;
|
|
214
|
-
project.cppHeaders.forEach(header => {
|
|
215
|
-
cppIncludes += `\n#include <${header}>`;
|
|
216
|
-
});
|
|
217
|
-
cppPackageProviders += `\n // IReactPackageProviders from ${dependencyName}`;
|
|
218
|
-
project.cppPackageProviders.forEach(packageProvider => {
|
|
219
|
-
cppPackageProviders += `\n packageProviders.Append(winrt::${packageProvider}());`;
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
if (cppPackageProviders === '') {
|
|
225
|
-
// There are no windows dependencies, this would result in warning. C4100: 'packageProviders': unreferenced formal parameter.
|
|
226
|
-
// therefore add a usage.
|
|
227
|
-
cppPackageProviders = '\n UNREFERENCED_PARAMETER(packageProviders);'; // CODESYNC: vnext\local-cli\generator-windows\index.js
|
|
228
|
-
}
|
|
229
|
-
return { cppPackageProviders, cppIncludes };
|
|
230
|
-
}
|
|
231
|
-
generateCSAutolinking(templateRoot, projectLang, projectDir) {
|
|
232
|
-
const { csUsingNamespaces, csReactPackageProviders } = this.getCsReplacements();
|
|
233
|
-
const csFileName = 'AutolinkedNativeModules.g.cs';
|
|
234
|
-
const srcCsFile = path_1.default.join(templateRoot, `${projectLang}-app`, 'src', csFileName);
|
|
235
|
-
const destCsFile = path_1.default.join(projectDir, csFileName);
|
|
236
|
-
verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(destCsFile))}...`, this.options.logging);
|
|
237
|
-
const csContents = generatorCommon.resolveContents(srcCsFile, {
|
|
238
|
-
useMustache: true,
|
|
239
|
-
autolinkCsUsingNamespaces: csUsingNamespaces,
|
|
240
|
-
autolinkCsReactPackageProviders: csReactPackageProviders,
|
|
241
|
-
});
|
|
242
|
-
return this.updateFile(destCsFile, csContents);
|
|
243
|
-
}
|
|
244
|
-
getCsReplacements() {
|
|
245
|
-
let csUsingNamespaces = '';
|
|
246
|
-
let csReactPackageProviders = '';
|
|
247
|
-
const windowsDependencies = this.getWindowsDependencies();
|
|
248
|
-
for (const dependencyName of Object.keys(windowsDependencies)) {
|
|
249
|
-
windowsDependencies[dependencyName].projects.forEach(project => {
|
|
250
|
-
if (project.directDependency) {
|
|
251
|
-
csUsingNamespaces += `\n\n// Namespaces from ${dependencyName}`;
|
|
252
|
-
project.csNamespaces.forEach(namespace => {
|
|
253
|
-
csUsingNamespaces += `\nusing ${namespace};`;
|
|
254
|
-
});
|
|
255
|
-
csReactPackageProviders += `\n // IReactPackageProviders from ${dependencyName}`;
|
|
256
|
-
project.csPackageProviders.forEach(packageProvider => {
|
|
257
|
-
csReactPackageProviders += `\n packageProviders.Add(new ${packageProvider}());`;
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
return { csUsingNamespaces, csReactPackageProviders };
|
|
263
|
-
}
|
|
264
|
-
getWindowsDependencies() {
|
|
265
|
-
if (!this.windowsDependencies) {
|
|
266
|
-
this.windowsDependencies = {};
|
|
267
|
-
for (const dependencyName of Object.keys(this.dependenciesConfig)) {
|
|
268
|
-
const windowsDependency = this.dependenciesConfig[dependencyName].platforms.windows;
|
|
269
|
-
if (windowsDependency) {
|
|
270
|
-
verboseMessage(`${chalk_1.default.bold(dependencyName)} has Windows implementation, config:`, this.options.logging);
|
|
271
|
-
verboseMessage(windowsDependency, this.options.logging);
|
|
272
|
-
let dependencyIsValid = false;
|
|
273
|
-
const hasValidSourceDir = 'sourceDir' in windowsDependency &&
|
|
274
|
-
windowsDependency.sourceDir !== undefined &&
|
|
275
|
-
!windowsDependency.sourceDir.startsWith('Error: ');
|
|
276
|
-
const hasProjectsInProjectsArray = 'projects' in windowsDependency &&
|
|
277
|
-
Array.isArray(windowsDependency.projects) &&
|
|
278
|
-
windowsDependency.projects.length > 0;
|
|
279
|
-
if (hasValidSourceDir && hasProjectsInProjectsArray) {
|
|
280
|
-
// Module is source-based and has projects
|
|
281
|
-
dependencyIsValid = true;
|
|
282
|
-
// Validate each source project
|
|
283
|
-
windowsDependency.projects.forEach(project => {
|
|
284
|
-
const itemsToCheck = [
|
|
285
|
-
'projectFile',
|
|
286
|
-
'directDependency',
|
|
287
|
-
];
|
|
288
|
-
itemsToCheck.forEach(item => {
|
|
289
|
-
dependencyIsValid = !!(dependencyIsValid &&
|
|
290
|
-
item in project &&
|
|
291
|
-
project[item] !== '' &&
|
|
292
|
-
!project[item].toString().startsWith('Error: '));
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
if (dependencyIsValid) {
|
|
297
|
-
verboseMessage(`Adding ${chalk_1.default.bold(dependencyName)}.`, this.options.logging);
|
|
298
|
-
this.windowsDependencies[dependencyName] = windowsDependency;
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
verboseMessage(`Invalid dependency configuration for dependency ${dependencyName}`, this.options.logging);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
return this.windowsDependencies;
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Updates the target file with the expected contents if it's different.
|
|
310
|
-
* @param filePath Path to the target file to update.
|
|
311
|
-
* @param expectedContents The expected contents of the file.
|
|
312
|
-
* @return Whether any changes were necessary.
|
|
313
|
-
*/
|
|
314
|
-
async updateFile(filePath, expectedContents) {
|
|
315
|
-
const fileName = chalk_1.default.bold(path_1.default.basename(filePath));
|
|
316
|
-
verboseMessage(`Reading ${fileName}...`, this.options.logging);
|
|
317
|
-
const actualContents = fs_1.default.existsSync(filePath)
|
|
318
|
-
? (await fs_1.default.readFile(filePath)).toString()
|
|
319
|
-
: '';
|
|
320
|
-
const contentsChanged = expectedContents !== actualContents;
|
|
321
|
-
if (contentsChanged) {
|
|
322
|
-
verboseMessage(chalk_1.default.yellow(`${fileName} needs to be updated.`), this.options.logging);
|
|
323
|
-
if (!this.options.check) {
|
|
324
|
-
verboseMessage(`Writing ${fileName}...`, this.options.logging);
|
|
325
|
-
await fs_1.default.writeFile(filePath, expectedContents, {
|
|
326
|
-
encoding: 'utf8',
|
|
327
|
-
flag: 'w',
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
else {
|
|
332
|
-
verboseMessage(`No changes to ${fileName}.`, this.options.logging);
|
|
333
|
-
}
|
|
334
|
-
return contentsChanged;
|
|
335
|
-
}
|
|
336
|
-
generateAutolinkTargets(projectDir, templateRoot) {
|
|
337
|
-
let projectReferencesForTargets = '';
|
|
338
|
-
const windowsDependencies = this.getWindowsDependencies();
|
|
339
|
-
for (const dependencyName of Object.keys(windowsDependencies)) {
|
|
340
|
-
windowsDependencies[dependencyName].projects.forEach(project => {
|
|
341
|
-
if (project.directDependency) {
|
|
342
|
-
const dependencyProjectFile = path_1.default.join(windowsDependencies[dependencyName].folder, windowsDependencies[dependencyName].sourceDir, project.projectFile);
|
|
343
|
-
const relDependencyProjectFile = path_1.default.relative(projectDir, dependencyProjectFile);
|
|
111
|
+
<ConsumeCSharpModules Condition="'$(ConsumeCSharpModules)'==''">true</ConsumeCSharpModules>`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
this.changesNecessary =
|
|
115
|
+
(await this.generateAutolinkProps(templateRoot, projectDir, propertiesForProps)) || this.changesNecessary;
|
|
116
|
+
// Generating targets for app project consumption
|
|
117
|
+
this.changesNecessary =
|
|
118
|
+
(await this.generateAutolinkTargets(projectDir, templateRoot)) ||
|
|
119
|
+
this.changesNecessary;
|
|
120
|
+
// Generating project entries for solution
|
|
121
|
+
this.changesNecessary =
|
|
122
|
+
this.updateSolution(rnwRoot, solutionFile) || this.changesNecessary;
|
|
123
|
+
spinner.succeed();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Handles the --proj command-line option by consuming its value into the windowsAppConfig
|
|
127
|
+
*/
|
|
128
|
+
fixUpForProjOption() {
|
|
129
|
+
if (this.options.proj) {
|
|
130
|
+
const projFile = path_1.default.join(this.windowsAppConfig.folder, this.options.proj);
|
|
131
|
+
const projectContents = configUtils.readProjectFile(projFile);
|
|
132
|
+
this.windowsAppConfig.project = {
|
|
133
|
+
projectFile: path_1.default.relative(path_1.default.join(this.windowsAppConfig.folder, this.windowsAppConfig.sourceDir), projFile),
|
|
134
|
+
projectName: configUtils.getProjectName(projFile, projectContents),
|
|
135
|
+
projectLang: configUtils.getProjectLanguage(projFile),
|
|
136
|
+
projectGuid: configUtils.getProjectGuid(projectContents),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Handles the --sln command-line option by consuming its value into the windowsAppConfig
|
|
142
|
+
*/
|
|
143
|
+
fixUpForSlnOption() {
|
|
144
|
+
if (this.options.sln) {
|
|
145
|
+
const slnFile = path_1.default.join(this.windowsAppConfig.folder, this.options.sln);
|
|
146
|
+
this.windowsAppConfig.solutionFile = path_1.default.relative(path_1.default.join(this.windowsAppConfig.folder, this.windowsAppConfig.sourceDir), slnFile);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/** Validates the all of the required app (solution) properties are present and valid */
|
|
150
|
+
validateRequiredAppProperties() {
|
|
151
|
+
const alwaysRequired = [
|
|
152
|
+
'folder',
|
|
153
|
+
'sourceDir',
|
|
154
|
+
'solutionFile',
|
|
155
|
+
'project',
|
|
156
|
+
];
|
|
157
|
+
alwaysRequired.forEach(item => {
|
|
158
|
+
if (!(item in this.windowsAppConfig) ||
|
|
159
|
+
this.windowsAppConfig[item] === null) {
|
|
160
|
+
throw new telemetry_1.CodedError('IncompleteConfig', `${item} is required but not specified by react-native config`, { item: item });
|
|
161
|
+
}
|
|
162
|
+
else if (typeof this.windowsAppConfig[item] === 'string' &&
|
|
163
|
+
this.windowsAppConfig[item].startsWith('Error: ')) {
|
|
164
|
+
throw new telemetry_1.CodedError('InvalidConfig', `${item} invalid. ${this.windowsAppConfig[item]}`, { item: item });
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/** @return the full path to the project file (.vcxproj or .csproj) */
|
|
169
|
+
getProjectFile() {
|
|
170
|
+
const windowsAppConfig = this.getWindowsConfig();
|
|
171
|
+
return path_1.default.join(windowsAppConfig.folder, windowsAppConfig.sourceDir, windowsAppConfig.project.projectFile);
|
|
172
|
+
}
|
|
173
|
+
/** Validates that all of the required app _project_ properties are present and valid */
|
|
174
|
+
validateRequiredProjectProperties() {
|
|
175
|
+
const windowsAppProjectConfig = this.windowsAppConfig.project;
|
|
176
|
+
const projectRequired = [
|
|
177
|
+
'projectFile',
|
|
178
|
+
'projectName',
|
|
179
|
+
'projectLang',
|
|
180
|
+
'projectGuid',
|
|
181
|
+
];
|
|
182
|
+
projectRequired.forEach(item => {
|
|
183
|
+
if (!(item in windowsAppProjectConfig) ||
|
|
184
|
+
windowsAppProjectConfig[item] === null) {
|
|
185
|
+
throw new telemetry_1.CodedError('IncompleteConfig', `project.${item} is required but not specified by react-native config`, { item: item });
|
|
186
|
+
}
|
|
187
|
+
else if (typeof windowsAppProjectConfig[item] === 'string' &&
|
|
188
|
+
windowsAppProjectConfig[item].startsWith('Error: ')) {
|
|
189
|
+
throw new telemetry_1.CodedError('InvalidConfig', `project.${item} invalid. ${windowsAppProjectConfig[item]}`, { item: item });
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async generateCppAutolinking(templateRoot, projectLang, projectDir) {
|
|
194
|
+
const { cppPackageProviders, cppIncludes } = this.getCppReplacements();
|
|
195
|
+
const cppFileName = 'AutolinkedNativeModules.g.cpp';
|
|
196
|
+
const srcCppFile = path_1.default.join(templateRoot, `${projectLang}-app`, 'src', cppFileName);
|
|
197
|
+
const destCppFile = path_1.default.join(projectDir, cppFileName);
|
|
198
|
+
verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(destCppFile))}...`, this.options.logging);
|
|
199
|
+
const cppContents = generatorCommon.resolveContents(srcCppFile, {
|
|
200
|
+
useMustache: true,
|
|
201
|
+
autolinkCppIncludes: cppIncludes,
|
|
202
|
+
autolinkCppPackageProviders: cppPackageProviders,
|
|
203
|
+
});
|
|
204
|
+
return await this.updateFile(destCppFile, cppContents);
|
|
205
|
+
}
|
|
206
|
+
getCppReplacements() {
|
|
207
|
+
let cppIncludes = '';
|
|
208
|
+
let cppPackageProviders = '';
|
|
209
|
+
const windowsDependencies = this.getWindowsDependencies();
|
|
210
|
+
for (const dependencyName of Object.keys(windowsDependencies)) {
|
|
211
|
+
windowsDependencies[dependencyName].projects.forEach(project => {
|
|
212
|
+
if (project.directDependency) {
|
|
213
|
+
cppIncludes += `\n\n// Includes from ${dependencyName}`;
|
|
214
|
+
project.cppHeaders.forEach(header => {
|
|
215
|
+
cppIncludes += `\n#include <${header}>`;
|
|
216
|
+
});
|
|
217
|
+
cppPackageProviders += `\n // IReactPackageProviders from ${dependencyName}`;
|
|
218
|
+
project.cppPackageProviders.forEach(packageProvider => {
|
|
219
|
+
cppPackageProviders += `\n packageProviders.Append(winrt::${packageProvider}());`;
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
if (cppPackageProviders === '') {
|
|
225
|
+
// There are no windows dependencies, this would result in warning. C4100: 'packageProviders': unreferenced formal parameter.
|
|
226
|
+
// therefore add a usage.
|
|
227
|
+
cppPackageProviders = '\n UNREFERENCED_PARAMETER(packageProviders);'; // CODESYNC: vnext\local-cli\generator-windows\index.js
|
|
228
|
+
}
|
|
229
|
+
return { cppPackageProviders, cppIncludes };
|
|
230
|
+
}
|
|
231
|
+
generateCSAutolinking(templateRoot, projectLang, projectDir) {
|
|
232
|
+
const { csUsingNamespaces, csReactPackageProviders } = this.getCsReplacements();
|
|
233
|
+
const csFileName = 'AutolinkedNativeModules.g.cs';
|
|
234
|
+
const srcCsFile = path_1.default.join(templateRoot, `${projectLang}-app`, 'src', csFileName);
|
|
235
|
+
const destCsFile = path_1.default.join(projectDir, csFileName);
|
|
236
|
+
verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(destCsFile))}...`, this.options.logging);
|
|
237
|
+
const csContents = generatorCommon.resolveContents(srcCsFile, {
|
|
238
|
+
useMustache: true,
|
|
239
|
+
autolinkCsUsingNamespaces: csUsingNamespaces,
|
|
240
|
+
autolinkCsReactPackageProviders: csReactPackageProviders,
|
|
241
|
+
});
|
|
242
|
+
return this.updateFile(destCsFile, csContents);
|
|
243
|
+
}
|
|
244
|
+
getCsReplacements() {
|
|
245
|
+
let csUsingNamespaces = '';
|
|
246
|
+
let csReactPackageProviders = '';
|
|
247
|
+
const windowsDependencies = this.getWindowsDependencies();
|
|
248
|
+
for (const dependencyName of Object.keys(windowsDependencies)) {
|
|
249
|
+
windowsDependencies[dependencyName].projects.forEach(project => {
|
|
250
|
+
if (project.directDependency) {
|
|
251
|
+
csUsingNamespaces += `\n\n// Namespaces from ${dependencyName}`;
|
|
252
|
+
project.csNamespaces.forEach(namespace => {
|
|
253
|
+
csUsingNamespaces += `\nusing ${namespace};`;
|
|
254
|
+
});
|
|
255
|
+
csReactPackageProviders += `\n // IReactPackageProviders from ${dependencyName}`;
|
|
256
|
+
project.csPackageProviders.forEach(packageProvider => {
|
|
257
|
+
csReactPackageProviders += `\n packageProviders.Add(new ${packageProvider}());`;
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
return { csUsingNamespaces, csReactPackageProviders };
|
|
263
|
+
}
|
|
264
|
+
getWindowsDependencies() {
|
|
265
|
+
if (!this.windowsDependencies) {
|
|
266
|
+
this.windowsDependencies = {};
|
|
267
|
+
for (const dependencyName of Object.keys(this.dependenciesConfig)) {
|
|
268
|
+
const windowsDependency = this.dependenciesConfig[dependencyName].platforms.windows;
|
|
269
|
+
if (windowsDependency) {
|
|
270
|
+
verboseMessage(`${chalk_1.default.bold(dependencyName)} has Windows implementation, config:`, this.options.logging);
|
|
271
|
+
verboseMessage(windowsDependency, this.options.logging);
|
|
272
|
+
let dependencyIsValid = false;
|
|
273
|
+
const hasValidSourceDir = 'sourceDir' in windowsDependency &&
|
|
274
|
+
windowsDependency.sourceDir !== undefined &&
|
|
275
|
+
!windowsDependency.sourceDir.startsWith('Error: ');
|
|
276
|
+
const hasProjectsInProjectsArray = 'projects' in windowsDependency &&
|
|
277
|
+
Array.isArray(windowsDependency.projects) &&
|
|
278
|
+
windowsDependency.projects.length > 0;
|
|
279
|
+
if (hasValidSourceDir && hasProjectsInProjectsArray) {
|
|
280
|
+
// Module is source-based and has projects
|
|
281
|
+
dependencyIsValid = true;
|
|
282
|
+
// Validate each source project
|
|
283
|
+
windowsDependency.projects.forEach(project => {
|
|
284
|
+
const itemsToCheck = [
|
|
285
|
+
'projectFile',
|
|
286
|
+
'directDependency',
|
|
287
|
+
];
|
|
288
|
+
itemsToCheck.forEach(item => {
|
|
289
|
+
dependencyIsValid = !!(dependencyIsValid &&
|
|
290
|
+
item in project &&
|
|
291
|
+
project[item] !== '' &&
|
|
292
|
+
!project[item].toString().startsWith('Error: '));
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
if (dependencyIsValid) {
|
|
297
|
+
verboseMessage(`Adding ${chalk_1.default.bold(dependencyName)}.`, this.options.logging);
|
|
298
|
+
this.windowsDependencies[dependencyName] = windowsDependency;
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
verboseMessage(`Invalid dependency configuration for dependency ${dependencyName}`, this.options.logging);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return this.windowsDependencies;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Updates the target file with the expected contents if it's different.
|
|
310
|
+
* @param filePath Path to the target file to update.
|
|
311
|
+
* @param expectedContents The expected contents of the file.
|
|
312
|
+
* @return Whether any changes were necessary.
|
|
313
|
+
*/
|
|
314
|
+
async updateFile(filePath, expectedContents) {
|
|
315
|
+
const fileName = chalk_1.default.bold(path_1.default.basename(filePath));
|
|
316
|
+
verboseMessage(`Reading ${fileName}...`, this.options.logging);
|
|
317
|
+
const actualContents = fs_1.default.existsSync(filePath)
|
|
318
|
+
? (await fs_1.default.readFile(filePath)).toString()
|
|
319
|
+
: '';
|
|
320
|
+
const contentsChanged = expectedContents !== actualContents;
|
|
321
|
+
if (contentsChanged) {
|
|
322
|
+
verboseMessage(chalk_1.default.yellow(`${fileName} needs to be updated.`), this.options.logging);
|
|
323
|
+
if (!this.options.check) {
|
|
324
|
+
verboseMessage(`Writing ${fileName}...`, this.options.logging);
|
|
325
|
+
await fs_1.default.writeFile(filePath, expectedContents, {
|
|
326
|
+
encoding: 'utf8',
|
|
327
|
+
flag: 'w',
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
verboseMessage(`No changes to ${fileName}.`, this.options.logging);
|
|
333
|
+
}
|
|
334
|
+
return contentsChanged;
|
|
335
|
+
}
|
|
336
|
+
generateAutolinkTargets(projectDir, templateRoot) {
|
|
337
|
+
let projectReferencesForTargets = '';
|
|
338
|
+
const windowsDependencies = this.getWindowsDependencies();
|
|
339
|
+
for (const dependencyName of Object.keys(windowsDependencies)) {
|
|
340
|
+
windowsDependencies[dependencyName].projects.forEach(project => {
|
|
341
|
+
if (project.directDependency) {
|
|
342
|
+
const dependencyProjectFile = path_1.default.join(windowsDependencies[dependencyName].folder, windowsDependencies[dependencyName].sourceDir, project.projectFile);
|
|
343
|
+
const relDependencyProjectFile = path_1.default.relative(projectDir, dependencyProjectFile);
|
|
344
344
|
projectReferencesForTargets += `
|
|
345
345
|
<!-- Projects from ${dependencyName} -->
|
|
346
346
|
<ProjectReference Include="$(ProjectDir)${relDependencyProjectFile}">
|
|
347
347
|
<Project>${project.projectGuid}</Project>
|
|
348
|
-
</ProjectReference>`;
|
|
349
|
-
}
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
const targetFileName = 'AutolinkedNativeModules.g.targets';
|
|
353
|
-
const srcTargetFile = path_1.default.join(templateRoot, `shared-app`, 'src', targetFileName);
|
|
354
|
-
const destTargetFile = path_1.default.join(projectDir, targetFileName);
|
|
355
|
-
verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(destTargetFile))}...`, this.options.logging);
|
|
356
|
-
const targetContents = generatorCommon.resolveContents(srcTargetFile, {
|
|
357
|
-
useMustache: true,
|
|
358
|
-
autolinkProjectReferencesForTargets: projectReferencesForTargets,
|
|
359
|
-
});
|
|
360
|
-
return this.updateFile(destTargetFile, targetContents);
|
|
361
|
-
}
|
|
362
|
-
generateAutolinkProps(templateRoot, projectDir, propertiesForProps) {
|
|
363
|
-
const propsFileName = 'AutolinkedNativeModules.g.props';
|
|
364
|
-
const srcPropsFile = path_1.default.join(templateRoot, `shared-app`, 'src', propsFileName);
|
|
365
|
-
const destPropsFile = path_1.default.join(projectDir, propsFileName);
|
|
366
|
-
verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(destPropsFile))}...`, this.options.logging);
|
|
367
|
-
const propsContents = generatorCommon.resolveContents(srcPropsFile, {
|
|
368
|
-
useMustache: true,
|
|
369
|
-
autolinkPropertiesForProps: propertiesForProps,
|
|
370
|
-
});
|
|
371
|
-
return this.updateFile(destPropsFile, propsContents);
|
|
372
|
-
}
|
|
373
|
-
getCSModules() {
|
|
374
|
-
const csModuleNames = [];
|
|
375
|
-
const windowsDependencies = this.getWindowsDependencies();
|
|
376
|
-
for (const dependencyName of Object.keys(windowsDependencies)) {
|
|
377
|
-
windowsDependencies[dependencyName].projects.forEach(project => {
|
|
378
|
-
if (project.directDependency && project.projectLang === 'cs') {
|
|
379
|
-
csModuleNames.push(project.projectName);
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
return csModuleNames;
|
|
384
|
-
}
|
|
385
|
-
updateSolution(rnwRoot, solutionFile) {
|
|
386
|
-
const projectsForSolution = [];
|
|
387
|
-
const windowsDependencies = this.getWindowsDependencies();
|
|
388
|
-
for (const dependencyName of Object.keys(windowsDependencies)) {
|
|
389
|
-
// Process dependency projects
|
|
390
|
-
windowsDependencies[dependencyName].projects.forEach(project => {
|
|
391
|
-
const dependencyProjectFile = path_1.default.join(windowsDependencies[dependencyName].folder, windowsDependencies[dependencyName].sourceDir, project.projectFile);
|
|
392
|
-
projectsForSolution.push({
|
|
393
|
-
projectFile: dependencyProjectFile,
|
|
394
|
-
projectName: project.projectName,
|
|
395
|
-
projectLang: project.projectLang,
|
|
396
|
-
projectGuid: project.projectGuid,
|
|
397
|
-
});
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
const csModuleNames = this.getCSModules();
|
|
401
|
-
if (csModuleNames.length > 0) {
|
|
402
|
-
// Add managed projects
|
|
403
|
-
projectsForSolution.push({
|
|
404
|
-
projectFile: path_1.default.join(rnwRoot, 'Microsoft.ReactNative.Managed/Microsoft.ReactNative.Managed.csproj'),
|
|
405
|
-
projectName: 'Microsoft.ReactNative.Managed',
|
|
406
|
-
projectLang: 'cs',
|
|
407
|
-
projectGuid: '{F2824844-CE15-4242-9420-308923CD76C3}',
|
|
408
|
-
});
|
|
409
|
-
projectsForSolution.push({
|
|
410
|
-
projectFile: path_1.default.join(rnwRoot, 'Microsoft.ReactNative.Managed.CodeGen//Microsoft.ReactNative.Managed.CodeGen.csproj'),
|
|
411
|
-
projectName: 'Microsoft.ReactNative.Managed.CodeGen',
|
|
412
|
-
projectLang: 'cs',
|
|
413
|
-
projectGuid: '{ADED4FBE-887D-4271-AF24-F0823BCE7961}',
|
|
414
|
-
projectTypeGuid: vstools.dotNetCoreProjectTypeGuid,
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(solutionFile))} changes...`, this.options.logging);
|
|
418
|
-
let changesNecessary = false;
|
|
419
|
-
projectsForSolution.forEach(project => {
|
|
420
|
-
const contentsChanged = vstools.addProjectToSolution(solutionFile, project, this.options.logging, this.options.check);
|
|
421
|
-
changesNecessary = changesNecessary || contentsChanged;
|
|
422
|
-
});
|
|
423
|
-
return changesNecessary;
|
|
424
|
-
}
|
|
425
|
-
getExperimentalFeaturesPropsXml() {
|
|
426
|
-
const experimentalFeaturesProps = path_1.default.join(path_1.default.dirname(this.getSolutionFile()), 'ExperimentalFeatures.props');
|
|
427
|
-
if (fs_1.default.existsSync(experimentalFeaturesProps)) {
|
|
428
|
-
const experimentalFeaturesContents = configUtils.readProjectFile(experimentalFeaturesProps);
|
|
429
|
-
return {
|
|
430
|
-
path: experimentalFeaturesProps,
|
|
431
|
-
content: experimentalFeaturesContents,
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
return undefined;
|
|
435
|
-
}
|
|
436
|
-
async ensureXAMLDialect() {
|
|
437
|
-
var _a, _b;
|
|
438
|
-
let changesNeeded = false;
|
|
439
|
-
const useWinUI3FromConfig = this.getWindowsConfig().useWinUI3;
|
|
440
|
-
const experimentalFeatures = this.getExperimentalFeaturesPropsXml();
|
|
441
|
-
if (experimentalFeatures) {
|
|
442
|
-
const useWinUI3FromExperimentalFeatures = ((_a = configUtils
|
|
443
|
-
.tryFindPropertyValue(experimentalFeatures.content, 'UseWinUI3')) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'true';
|
|
444
|
-
// Check if WinUI2xVersion is specified in experimental features
|
|
445
|
-
const targetWinUI2xVersion = configUtils.tryFindPropertyValue(experimentalFeatures.content, 'WinUI2xVersion');
|
|
446
|
-
// Check if WinUI3Version is specified in experimental features
|
|
447
|
-
const targetWinUI3xVersion = configUtils.tryFindPropertyValue(experimentalFeatures.content, 'WinUI3Version');
|
|
448
|
-
// Use the UseWinUI3 value in react-native.config.js, or if not present, the value from ExperimentalFeatures.props
|
|
449
|
-
changesNeeded = await this.updatePackagesConfigXAMLDialect(useWinUI3FromConfig !== undefined
|
|
450
|
-
? useWinUI3FromConfig
|
|
451
|
-
: useWinUI3FromExperimentalFeatures, targetWinUI2xVersion, targetWinUI3xVersion);
|
|
452
|
-
if (useWinUI3FromConfig !== undefined) {
|
|
453
|
-
// Make sure ExperimentalFeatures.props matches the value that comes from react-native.config.js
|
|
454
|
-
const node = experimentalFeatures.content.getElementsByTagName('UseWinUI3');
|
|
455
|
-
const newValue = useWinUI3FromConfig ? 'true' : 'false';
|
|
456
|
-
changesNeeded = ((_b = node.item(0)) === null || _b === void 0 ? void 0 : _b.textContent) !== newValue || changesNeeded;
|
|
457
|
-
if (!this.options.check && changesNeeded) {
|
|
458
|
-
node.item(0).textContent = newValue;
|
|
459
|
-
const experimentalFeaturesOutput = new xmldom_1.XMLSerializer().serializeToString(experimentalFeatures.content);
|
|
460
|
-
await this.updateFile(experimentalFeatures.path, experimentalFeaturesOutput);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
return changesNeeded;
|
|
465
|
-
}
|
|
466
|
-
getPackagesConfigXml() {
|
|
467
|
-
const projectFile = this.getProjectFile();
|
|
468
|
-
const packagesConfig = path_1.default.join(path_1.default.dirname(projectFile), 'packages.config');
|
|
469
|
-
if (fs_1.default.existsSync(packagesConfig)) {
|
|
470
|
-
return {
|
|
471
|
-
path: packagesConfig,
|
|
472
|
-
content: configUtils.readProjectFile(packagesConfig),
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
return undefined;
|
|
476
|
-
}
|
|
477
|
-
async updatePackagesConfigXAMLDialect(useWinUI3, targetWinUI2xVersion, targetWinUI3xVersion) {
|
|
478
|
-
let changed = false;
|
|
479
|
-
const packagesConfig = this.getPackagesConfigXml();
|
|
480
|
-
if (packagesConfig) {
|
|
481
|
-
// if we don't have a packages.config, then this is a C# project, in which case we use <PackageReference> and dynamically pick the right XAML package.
|
|
482
|
-
const project = this.getWindowsConfig();
|
|
483
|
-
const winUIPropsPath = path_1.default.join(resolveRnwRoot(project), 'PropertySheets/WinUI.props');
|
|
484
|
-
const winuiPropsContents = configUtils.readProjectFile(winUIPropsPath);
|
|
485
|
-
// Use the given WinUI2xVersion, otherwise fallback to WinUI.props
|
|
486
|
-
const winui2xVersion = targetWinUI2xVersion !== null && targetWinUI2xVersion !== void 0 ? targetWinUI2xVersion : configUtils.tryFindPropertyValue(winuiPropsContents, 'WinUI2xVersion');
|
|
487
|
-
// Use the given WinUI3Version, otherwise fallback to WinUI.props
|
|
488
|
-
const winui3Version = targetWinUI3xVersion !== null && targetWinUI3xVersion !== void 0 ? targetWinUI3xVersion : configUtils.tryFindPropertyValue(winuiPropsContents, 'WinUI3Version');
|
|
489
|
-
const dialects = [
|
|
490
|
-
{ id: 'Microsoft.WindowsAppSDK', version: winui3Version },
|
|
491
|
-
{ id: 'Microsoft.UI.Xaml', version: winui2xVersion },
|
|
492
|
-
];
|
|
493
|
-
const keepPkg = useWinUI3 ? dialects[0] : dialects[1];
|
|
494
|
-
const removePkg = useWinUI3 ? dialects[1] : dialects[0];
|
|
495
|
-
changed = this.updatePackagesConfig(packagesConfig, [removePkg], [keepPkg]);
|
|
496
|
-
if (!this.options.check && changed) {
|
|
497
|
-
const serializer = new xmldom_1.XMLSerializer();
|
|
498
|
-
const output = serializer.serializeToString(packagesConfig.content);
|
|
499
|
-
const formattedXml = formatter(output, { indentation: ' ' });
|
|
500
|
-
await this.updateFile(packagesConfig.path, formattedXml);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
return changed;
|
|
504
|
-
}
|
|
505
|
-
updatePackagesConfig(packagesConfig, removePkgs, keepPkgs) {
|
|
506
|
-
let changed = false;
|
|
507
|
-
const packageElements = packagesConfig.content.documentElement.getElementsByTagName('package');
|
|
508
|
-
const nodesToRemove = [];
|
|
509
|
-
for (let i = 0; i < packageElements.length; i++) {
|
|
510
|
-
const packageElement = packageElements.item(i);
|
|
511
|
-
const idAttr = packageElement.getAttributeNode('id');
|
|
512
|
-
const id = idAttr.value;
|
|
513
|
-
const keepPkg = keepPkgs.find(pkg => pkg.id === id);
|
|
514
|
-
if (removePkgs.find(pkg => pkg.id === id)) {
|
|
515
|
-
nodesToRemove.push(packageElement);
|
|
516
|
-
changed = true;
|
|
517
|
-
}
|
|
518
|
-
else if (keepPkg) {
|
|
519
|
-
changed =
|
|
520
|
-
changed || keepPkg.version !== packageElement.getAttribute('version');
|
|
521
|
-
packageElement.setAttribute('version', keepPkg.version);
|
|
522
|
-
keepPkgs = keepPkgs.filter(pkg => pkg.id !== keepPkg.id);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
nodesToRemove.forEach(pkg => packagesConfig.content.documentElement.removeChild(pkg));
|
|
526
|
-
keepPkgs.forEach(keepPkg => {
|
|
527
|
-
const newPkg = packagesConfig.content.createElement('package');
|
|
528
|
-
Object.entries(keepPkg).forEach(([attr, value]) => {
|
|
529
|
-
newPkg.setAttribute(attr, value);
|
|
530
|
-
});
|
|
531
|
-
newPkg.setAttribute('targetFramework', 'native');
|
|
532
|
-
packagesConfig.content.documentElement.appendChild(newPkg);
|
|
533
|
-
changed = true;
|
|
534
|
-
});
|
|
535
|
-
return changed;
|
|
536
|
-
}
|
|
537
|
-
/** @return The CLI command to invoke autolink-windows independently */
|
|
538
|
-
getAutolinkWindowsCommand() {
|
|
539
|
-
const folder = this.windowsAppConfig.folder;
|
|
540
|
-
const autolinkCommand = 'npx react-native autolink-windows';
|
|
541
|
-
const autolinkArgs = `--sln "${path_1.default.relative(folder, this.getSolutionFile())}" --proj "${path_1.default.relative(folder, this.getProjectFile())}"`;
|
|
542
|
-
return `${autolinkCommand} ${autolinkArgs}`;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
exports.AutoLinkWindows = AutoLinkWindows;
|
|
546
|
-
/**
|
|
547
|
-
* Locates the react-native-windows directory
|
|
548
|
-
* @param config project configuration
|
|
549
|
-
*/
|
|
550
|
-
function resolveRnwRoot(projectConfig) {
|
|
551
|
-
return pathHelpers.resolveRnwRoot(projectConfig.folder);
|
|
552
|
-
}
|
|
553
|
-
/**
|
|
554
|
-
* Locates the react-native-windows directory containing template files
|
|
555
|
-
* @param config project configuration
|
|
556
|
-
*/
|
|
557
|
-
function resolveTemplateRoot(projectConfig) {
|
|
558
|
-
const rnwPackage = resolveRnwRoot(projectConfig);
|
|
559
|
-
return path_1.default.join(rnwPackage, 'template');
|
|
560
|
-
}
|
|
561
|
-
/**
|
|
562
|
-
* Logs the given message if verbose is True.
|
|
563
|
-
* @param message The message to log.
|
|
564
|
-
* @param verbose Whether or not verbose logging is enabled.
|
|
565
|
-
*/
|
|
566
|
-
function verboseMessage(message, verbose) {
|
|
567
|
-
if (verbose) {
|
|
568
|
-
console.log(message);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
/**
|
|
572
|
-
* Sanitizes the given option for telemetry.
|
|
573
|
-
* @param key The key of the option.
|
|
574
|
-
* @param value The unsanitized value of the option.
|
|
575
|
-
* @returns The sanitized value of the option.
|
|
576
|
-
*/
|
|
577
|
-
function optionSanitizer(key, value) {
|
|
578
|
-
// Do not add a default case here.
|
|
579
|
-
// Strings risking PII should just return true if present, false otherwise.
|
|
580
|
-
// All others should return the value (or false if undefined).
|
|
581
|
-
switch (key) {
|
|
582
|
-
case 'sln':
|
|
583
|
-
case 'proj':
|
|
584
|
-
return value === undefined ? false : true; // Strip PII
|
|
585
|
-
case 'logging':
|
|
586
|
-
case 'check':
|
|
587
|
-
case 'telemetry':
|
|
588
|
-
return value === undefined ? false : value; // Return value
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
/**
|
|
592
|
-
* Get the extra props to add to the `autolink-windows` telemetry event.
|
|
593
|
-
* @returns The extra props.
|
|
594
|
-
*/
|
|
595
|
-
async function getExtraProps() {
|
|
596
|
-
const extraProps = {};
|
|
597
|
-
return extraProps;
|
|
598
|
-
}
|
|
599
|
-
/**
|
|
600
|
-
* The function run when calling `react-native autolink-windows`.
|
|
601
|
-
* @param args Unprocessed args passed from react-native CLI.
|
|
602
|
-
* @param config Config passed from react-native CLI.
|
|
603
|
-
* @param options Options passed from react-native CLI.
|
|
604
|
-
*/
|
|
605
|
-
async function autolinkWindows(args, config, options) {
|
|
606
|
-
await (0, telemetryHelpers_1.startTelemetrySession)('autolink-windows', config, options, (0, telemetryHelpers_1.getDefaultOptions)(config, autolinkWindowsOptions_1.autolinkOptions), optionSanitizer);
|
|
607
|
-
let autolinkWindowsError;
|
|
608
|
-
try {
|
|
609
|
-
await autolinkWindowsInternal(args, config, options);
|
|
610
|
-
}
|
|
611
|
-
catch (ex) {
|
|
612
|
-
autolinkWindowsError =
|
|
613
|
-
ex instanceof Error ? ex : new Error(String(ex));
|
|
614
|
-
telemetry_1.Telemetry.trackException(autolinkWindowsError);
|
|
615
|
-
}
|
|
616
|
-
await (0, telemetryHelpers_1.endTelemetrySession)(autolinkWindowsError, getExtraProps);
|
|
617
|
-
(0, commandWithProgress_1.setExitProcessWithError)(options.logging, autolinkWindowsError);
|
|
618
|
-
}
|
|
619
|
-
/**
|
|
620
|
-
* Performs auto-linking for RNW native modules and apps.
|
|
621
|
-
* @param args Unprocessed args passed from react-native CLI.
|
|
622
|
-
* @param config Config passed from react-native CLI.
|
|
623
|
-
* @param options Options passed from react-native CLI.
|
|
624
|
-
*/
|
|
625
|
-
async function autolinkWindowsInternal(args, config, options) {
|
|
626
|
-
const startTime = perf_hooks_1.performance.now();
|
|
627
|
-
const spinner = (0, commandWithProgress_1.newSpinner)(options.check ? 'Checking auto-linked files...' : 'Auto-linking...');
|
|
628
|
-
try {
|
|
629
|
-
const autolink = new AutoLinkWindows(config.project, config.dependencies, options);
|
|
630
|
-
await autolink.run(spinner);
|
|
631
|
-
const endTime = perf_hooks_1.performance.now();
|
|
632
|
-
if (!autolink.areChangesNeeded()) {
|
|
633
|
-
console.log(`${chalk_1.default.green('Success:')} No auto-linking changes necessary. (${Math.round(endTime - startTime)}ms)`);
|
|
634
|
-
}
|
|
635
|
-
else if (options.check) {
|
|
636
|
-
const autolinkCommand = autolink.getAutolinkWindowsCommand();
|
|
637
|
-
console.log(`${chalk_1.default.yellow('Warning:')} Auto-linking changes were necessary but ${chalk_1.default.bold('--check')} specified. Run '${chalk_1.default.bold(`${autolinkCommand}`)}' to apply the changes. (${Math.round(endTime - startTime)}ms)`);
|
|
638
|
-
throw new telemetry_1.CodedError('NeedAutolinking', `Auto-linking changes were necessary but --check was specified. Run '${autolinkCommand}' to apply the changes`);
|
|
639
|
-
}
|
|
640
|
-
else {
|
|
641
|
-
console.log(`${chalk_1.default.green('Success:')} Auto-linking changes completed. (${Math.round(endTime - startTime)}ms)`);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
catch (e) {
|
|
645
|
-
spinner.fail();
|
|
646
|
-
const endTime = perf_hooks_1.performance.now();
|
|
647
|
-
console.log(`${chalk_1.default.red('Error:')} ${e.toString()}. (${Math.round(endTime - startTime)}ms)`);
|
|
648
|
-
throw e;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
exports.autolinkWindowsInternal = autolinkWindowsInternal;
|
|
652
|
-
/**
|
|
653
|
-
* Performs auto-linking for RNW native modules and apps.
|
|
654
|
-
*/
|
|
655
|
-
exports.autolinkCommand = {
|
|
656
|
-
name: 'autolink-windows',
|
|
657
|
-
description: 'Runs Windows-specific autolinking',
|
|
658
|
-
func: autolinkWindows,
|
|
659
|
-
options: autolinkWindowsOptions_1.autolinkOptions,
|
|
660
|
-
};
|
|
348
|
+
</ProjectReference>`;
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
const targetFileName = 'AutolinkedNativeModules.g.targets';
|
|
353
|
+
const srcTargetFile = path_1.default.join(templateRoot, `shared-app`, 'src', targetFileName);
|
|
354
|
+
const destTargetFile = path_1.default.join(projectDir, targetFileName);
|
|
355
|
+
verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(destTargetFile))}...`, this.options.logging);
|
|
356
|
+
const targetContents = generatorCommon.resolveContents(srcTargetFile, {
|
|
357
|
+
useMustache: true,
|
|
358
|
+
autolinkProjectReferencesForTargets: projectReferencesForTargets,
|
|
359
|
+
});
|
|
360
|
+
return this.updateFile(destTargetFile, targetContents);
|
|
361
|
+
}
|
|
362
|
+
generateAutolinkProps(templateRoot, projectDir, propertiesForProps) {
|
|
363
|
+
const propsFileName = 'AutolinkedNativeModules.g.props';
|
|
364
|
+
const srcPropsFile = path_1.default.join(templateRoot, `shared-app`, 'src', propsFileName);
|
|
365
|
+
const destPropsFile = path_1.default.join(projectDir, propsFileName);
|
|
366
|
+
verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(destPropsFile))}...`, this.options.logging);
|
|
367
|
+
const propsContents = generatorCommon.resolveContents(srcPropsFile, {
|
|
368
|
+
useMustache: true,
|
|
369
|
+
autolinkPropertiesForProps: propertiesForProps,
|
|
370
|
+
});
|
|
371
|
+
return this.updateFile(destPropsFile, propsContents);
|
|
372
|
+
}
|
|
373
|
+
getCSModules() {
|
|
374
|
+
const csModuleNames = [];
|
|
375
|
+
const windowsDependencies = this.getWindowsDependencies();
|
|
376
|
+
for (const dependencyName of Object.keys(windowsDependencies)) {
|
|
377
|
+
windowsDependencies[dependencyName].projects.forEach(project => {
|
|
378
|
+
if (project.directDependency && project.projectLang === 'cs') {
|
|
379
|
+
csModuleNames.push(project.projectName);
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
return csModuleNames;
|
|
384
|
+
}
|
|
385
|
+
updateSolution(rnwRoot, solutionFile) {
|
|
386
|
+
const projectsForSolution = [];
|
|
387
|
+
const windowsDependencies = this.getWindowsDependencies();
|
|
388
|
+
for (const dependencyName of Object.keys(windowsDependencies)) {
|
|
389
|
+
// Process dependency projects
|
|
390
|
+
windowsDependencies[dependencyName].projects.forEach(project => {
|
|
391
|
+
const dependencyProjectFile = path_1.default.join(windowsDependencies[dependencyName].folder, windowsDependencies[dependencyName].sourceDir, project.projectFile);
|
|
392
|
+
projectsForSolution.push({
|
|
393
|
+
projectFile: dependencyProjectFile,
|
|
394
|
+
projectName: project.projectName,
|
|
395
|
+
projectLang: project.projectLang,
|
|
396
|
+
projectGuid: project.projectGuid,
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
const csModuleNames = this.getCSModules();
|
|
401
|
+
if (csModuleNames.length > 0) {
|
|
402
|
+
// Add managed projects
|
|
403
|
+
projectsForSolution.push({
|
|
404
|
+
projectFile: path_1.default.join(rnwRoot, 'Microsoft.ReactNative.Managed/Microsoft.ReactNative.Managed.csproj'),
|
|
405
|
+
projectName: 'Microsoft.ReactNative.Managed',
|
|
406
|
+
projectLang: 'cs',
|
|
407
|
+
projectGuid: '{F2824844-CE15-4242-9420-308923CD76C3}',
|
|
408
|
+
});
|
|
409
|
+
projectsForSolution.push({
|
|
410
|
+
projectFile: path_1.default.join(rnwRoot, 'Microsoft.ReactNative.Managed.CodeGen//Microsoft.ReactNative.Managed.CodeGen.csproj'),
|
|
411
|
+
projectName: 'Microsoft.ReactNative.Managed.CodeGen',
|
|
412
|
+
projectLang: 'cs',
|
|
413
|
+
projectGuid: '{ADED4FBE-887D-4271-AF24-F0823BCE7961}',
|
|
414
|
+
projectTypeGuid: vstools.dotNetCoreProjectTypeGuid,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(solutionFile))} changes...`, this.options.logging);
|
|
418
|
+
let changesNecessary = false;
|
|
419
|
+
projectsForSolution.forEach(project => {
|
|
420
|
+
const contentsChanged = vstools.addProjectToSolution(solutionFile, project, this.options.logging, this.options.check);
|
|
421
|
+
changesNecessary = changesNecessary || contentsChanged;
|
|
422
|
+
});
|
|
423
|
+
return changesNecessary;
|
|
424
|
+
}
|
|
425
|
+
getExperimentalFeaturesPropsXml() {
|
|
426
|
+
const experimentalFeaturesProps = path_1.default.join(path_1.default.dirname(this.getSolutionFile()), 'ExperimentalFeatures.props');
|
|
427
|
+
if (fs_1.default.existsSync(experimentalFeaturesProps)) {
|
|
428
|
+
const experimentalFeaturesContents = configUtils.readProjectFile(experimentalFeaturesProps);
|
|
429
|
+
return {
|
|
430
|
+
path: experimentalFeaturesProps,
|
|
431
|
+
content: experimentalFeaturesContents,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
return undefined;
|
|
435
|
+
}
|
|
436
|
+
async ensureXAMLDialect() {
|
|
437
|
+
var _a, _b;
|
|
438
|
+
let changesNeeded = false;
|
|
439
|
+
const useWinUI3FromConfig = this.getWindowsConfig().useWinUI3;
|
|
440
|
+
const experimentalFeatures = this.getExperimentalFeaturesPropsXml();
|
|
441
|
+
if (experimentalFeatures) {
|
|
442
|
+
const useWinUI3FromExperimentalFeatures = ((_a = configUtils
|
|
443
|
+
.tryFindPropertyValue(experimentalFeatures.content, 'UseWinUI3')) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'true';
|
|
444
|
+
// Check if WinUI2xVersion is specified in experimental features
|
|
445
|
+
const targetWinUI2xVersion = configUtils.tryFindPropertyValue(experimentalFeatures.content, 'WinUI2xVersion');
|
|
446
|
+
// Check if WinUI3Version is specified in experimental features
|
|
447
|
+
const targetWinUI3xVersion = configUtils.tryFindPropertyValue(experimentalFeatures.content, 'WinUI3Version');
|
|
448
|
+
// Use the UseWinUI3 value in react-native.config.js, or if not present, the value from ExperimentalFeatures.props
|
|
449
|
+
changesNeeded = await this.updatePackagesConfigXAMLDialect(useWinUI3FromConfig !== undefined
|
|
450
|
+
? useWinUI3FromConfig
|
|
451
|
+
: useWinUI3FromExperimentalFeatures, targetWinUI2xVersion, targetWinUI3xVersion);
|
|
452
|
+
if (useWinUI3FromConfig !== undefined) {
|
|
453
|
+
// Make sure ExperimentalFeatures.props matches the value that comes from react-native.config.js
|
|
454
|
+
const node = experimentalFeatures.content.getElementsByTagName('UseWinUI3');
|
|
455
|
+
const newValue = useWinUI3FromConfig ? 'true' : 'false';
|
|
456
|
+
changesNeeded = ((_b = node.item(0)) === null || _b === void 0 ? void 0 : _b.textContent) !== newValue || changesNeeded;
|
|
457
|
+
if (!this.options.check && changesNeeded) {
|
|
458
|
+
node.item(0).textContent = newValue;
|
|
459
|
+
const experimentalFeaturesOutput = new xmldom_1.XMLSerializer().serializeToString(experimentalFeatures.content);
|
|
460
|
+
await this.updateFile(experimentalFeatures.path, experimentalFeaturesOutput);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return changesNeeded;
|
|
465
|
+
}
|
|
466
|
+
getPackagesConfigXml() {
|
|
467
|
+
const projectFile = this.getProjectFile();
|
|
468
|
+
const packagesConfig = path_1.default.join(path_1.default.dirname(projectFile), 'packages.config');
|
|
469
|
+
if (fs_1.default.existsSync(packagesConfig)) {
|
|
470
|
+
return {
|
|
471
|
+
path: packagesConfig,
|
|
472
|
+
content: configUtils.readProjectFile(packagesConfig),
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
return undefined;
|
|
476
|
+
}
|
|
477
|
+
async updatePackagesConfigXAMLDialect(useWinUI3, targetWinUI2xVersion, targetWinUI3xVersion) {
|
|
478
|
+
let changed = false;
|
|
479
|
+
const packagesConfig = this.getPackagesConfigXml();
|
|
480
|
+
if (packagesConfig) {
|
|
481
|
+
// if we don't have a packages.config, then this is a C# project, in which case we use <PackageReference> and dynamically pick the right XAML package.
|
|
482
|
+
const project = this.getWindowsConfig();
|
|
483
|
+
const winUIPropsPath = path_1.default.join(resolveRnwRoot(project), 'PropertySheets/WinUI.props');
|
|
484
|
+
const winuiPropsContents = configUtils.readProjectFile(winUIPropsPath);
|
|
485
|
+
// Use the given WinUI2xVersion, otherwise fallback to WinUI.props
|
|
486
|
+
const winui2xVersion = targetWinUI2xVersion !== null && targetWinUI2xVersion !== void 0 ? targetWinUI2xVersion : configUtils.tryFindPropertyValue(winuiPropsContents, 'WinUI2xVersion');
|
|
487
|
+
// Use the given WinUI3Version, otherwise fallback to WinUI.props
|
|
488
|
+
const winui3Version = targetWinUI3xVersion !== null && targetWinUI3xVersion !== void 0 ? targetWinUI3xVersion : configUtils.tryFindPropertyValue(winuiPropsContents, 'WinUI3Version');
|
|
489
|
+
const dialects = [
|
|
490
|
+
{ id: 'Microsoft.WindowsAppSDK', version: winui3Version },
|
|
491
|
+
{ id: 'Microsoft.UI.Xaml', version: winui2xVersion },
|
|
492
|
+
];
|
|
493
|
+
const keepPkg = useWinUI3 ? dialects[0] : dialects[1];
|
|
494
|
+
const removePkg = useWinUI3 ? dialects[1] : dialects[0];
|
|
495
|
+
changed = this.updatePackagesConfig(packagesConfig, [removePkg], [keepPkg]);
|
|
496
|
+
if (!this.options.check && changed) {
|
|
497
|
+
const serializer = new xmldom_1.XMLSerializer();
|
|
498
|
+
const output = serializer.serializeToString(packagesConfig.content);
|
|
499
|
+
const formattedXml = formatter(output, { indentation: ' ' });
|
|
500
|
+
await this.updateFile(packagesConfig.path, formattedXml);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return changed;
|
|
504
|
+
}
|
|
505
|
+
updatePackagesConfig(packagesConfig, removePkgs, keepPkgs) {
|
|
506
|
+
let changed = false;
|
|
507
|
+
const packageElements = packagesConfig.content.documentElement.getElementsByTagName('package');
|
|
508
|
+
const nodesToRemove = [];
|
|
509
|
+
for (let i = 0; i < packageElements.length; i++) {
|
|
510
|
+
const packageElement = packageElements.item(i);
|
|
511
|
+
const idAttr = packageElement.getAttributeNode('id');
|
|
512
|
+
const id = idAttr.value;
|
|
513
|
+
const keepPkg = keepPkgs.find(pkg => pkg.id === id);
|
|
514
|
+
if (removePkgs.find(pkg => pkg.id === id)) {
|
|
515
|
+
nodesToRemove.push(packageElement);
|
|
516
|
+
changed = true;
|
|
517
|
+
}
|
|
518
|
+
else if (keepPkg) {
|
|
519
|
+
changed =
|
|
520
|
+
changed || keepPkg.version !== packageElement.getAttribute('version');
|
|
521
|
+
packageElement.setAttribute('version', keepPkg.version);
|
|
522
|
+
keepPkgs = keepPkgs.filter(pkg => pkg.id !== keepPkg.id);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
nodesToRemove.forEach(pkg => packagesConfig.content.documentElement.removeChild(pkg));
|
|
526
|
+
keepPkgs.forEach(keepPkg => {
|
|
527
|
+
const newPkg = packagesConfig.content.createElement('package');
|
|
528
|
+
Object.entries(keepPkg).forEach(([attr, value]) => {
|
|
529
|
+
newPkg.setAttribute(attr, value);
|
|
530
|
+
});
|
|
531
|
+
newPkg.setAttribute('targetFramework', 'native');
|
|
532
|
+
packagesConfig.content.documentElement.appendChild(newPkg);
|
|
533
|
+
changed = true;
|
|
534
|
+
});
|
|
535
|
+
return changed;
|
|
536
|
+
}
|
|
537
|
+
/** @return The CLI command to invoke autolink-windows independently */
|
|
538
|
+
getAutolinkWindowsCommand() {
|
|
539
|
+
const folder = this.windowsAppConfig.folder;
|
|
540
|
+
const autolinkCommand = 'npx react-native autolink-windows';
|
|
541
|
+
const autolinkArgs = `--sln "${path_1.default.relative(folder, this.getSolutionFile())}" --proj "${path_1.default.relative(folder, this.getProjectFile())}"`;
|
|
542
|
+
return `${autolinkCommand} ${autolinkArgs}`;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
exports.AutoLinkWindows = AutoLinkWindows;
|
|
546
|
+
/**
|
|
547
|
+
* Locates the react-native-windows directory
|
|
548
|
+
* @param config project configuration
|
|
549
|
+
*/
|
|
550
|
+
function resolveRnwRoot(projectConfig) {
|
|
551
|
+
return pathHelpers.resolveRnwRoot(projectConfig.folder);
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Locates the react-native-windows directory containing template files
|
|
555
|
+
* @param config project configuration
|
|
556
|
+
*/
|
|
557
|
+
function resolveTemplateRoot(projectConfig) {
|
|
558
|
+
const rnwPackage = resolveRnwRoot(projectConfig);
|
|
559
|
+
return path_1.default.join(rnwPackage, 'template');
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Logs the given message if verbose is True.
|
|
563
|
+
* @param message The message to log.
|
|
564
|
+
* @param verbose Whether or not verbose logging is enabled.
|
|
565
|
+
*/
|
|
566
|
+
function verboseMessage(message, verbose) {
|
|
567
|
+
if (verbose) {
|
|
568
|
+
console.log(message);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Sanitizes the given option for telemetry.
|
|
573
|
+
* @param key The key of the option.
|
|
574
|
+
* @param value The unsanitized value of the option.
|
|
575
|
+
* @returns The sanitized value of the option.
|
|
576
|
+
*/
|
|
577
|
+
function optionSanitizer(key, value) {
|
|
578
|
+
// Do not add a default case here.
|
|
579
|
+
// Strings risking PII should just return true if present, false otherwise.
|
|
580
|
+
// All others should return the value (or false if undefined).
|
|
581
|
+
switch (key) {
|
|
582
|
+
case 'sln':
|
|
583
|
+
case 'proj':
|
|
584
|
+
return value === undefined ? false : true; // Strip PII
|
|
585
|
+
case 'logging':
|
|
586
|
+
case 'check':
|
|
587
|
+
case 'telemetry':
|
|
588
|
+
return value === undefined ? false : value; // Return value
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Get the extra props to add to the `autolink-windows` telemetry event.
|
|
593
|
+
* @returns The extra props.
|
|
594
|
+
*/
|
|
595
|
+
async function getExtraProps() {
|
|
596
|
+
const extraProps = {};
|
|
597
|
+
return extraProps;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* The function run when calling `react-native autolink-windows`.
|
|
601
|
+
* @param args Unprocessed args passed from react-native CLI.
|
|
602
|
+
* @param config Config passed from react-native CLI.
|
|
603
|
+
* @param options Options passed from react-native CLI.
|
|
604
|
+
*/
|
|
605
|
+
async function autolinkWindows(args, config, options) {
|
|
606
|
+
await (0, telemetryHelpers_1.startTelemetrySession)('autolink-windows', config, options, (0, telemetryHelpers_1.getDefaultOptions)(config, autolinkWindowsOptions_1.autolinkOptions), optionSanitizer);
|
|
607
|
+
let autolinkWindowsError;
|
|
608
|
+
try {
|
|
609
|
+
await autolinkWindowsInternal(args, config, options);
|
|
610
|
+
}
|
|
611
|
+
catch (ex) {
|
|
612
|
+
autolinkWindowsError =
|
|
613
|
+
ex instanceof Error ? ex : new Error(String(ex));
|
|
614
|
+
telemetry_1.Telemetry.trackException(autolinkWindowsError);
|
|
615
|
+
}
|
|
616
|
+
await (0, telemetryHelpers_1.endTelemetrySession)(autolinkWindowsError, getExtraProps);
|
|
617
|
+
(0, commandWithProgress_1.setExitProcessWithError)(options.logging, autolinkWindowsError);
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Performs auto-linking for RNW native modules and apps.
|
|
621
|
+
* @param args Unprocessed args passed from react-native CLI.
|
|
622
|
+
* @param config Config passed from react-native CLI.
|
|
623
|
+
* @param options Options passed from react-native CLI.
|
|
624
|
+
*/
|
|
625
|
+
async function autolinkWindowsInternal(args, config, options) {
|
|
626
|
+
const startTime = perf_hooks_1.performance.now();
|
|
627
|
+
const spinner = (0, commandWithProgress_1.newSpinner)(options.check ? 'Checking auto-linked files...' : 'Auto-linking...');
|
|
628
|
+
try {
|
|
629
|
+
const autolink = new AutoLinkWindows(config.project, config.dependencies, options);
|
|
630
|
+
await autolink.run(spinner);
|
|
631
|
+
const endTime = perf_hooks_1.performance.now();
|
|
632
|
+
if (!autolink.areChangesNeeded()) {
|
|
633
|
+
console.log(`${chalk_1.default.green('Success:')} No auto-linking changes necessary. (${Math.round(endTime - startTime)}ms)`);
|
|
634
|
+
}
|
|
635
|
+
else if (options.check) {
|
|
636
|
+
const autolinkCommand = autolink.getAutolinkWindowsCommand();
|
|
637
|
+
console.log(`${chalk_1.default.yellow('Warning:')} Auto-linking changes were necessary but ${chalk_1.default.bold('--check')} specified. Run '${chalk_1.default.bold(`${autolinkCommand}`)}' to apply the changes. (${Math.round(endTime - startTime)}ms)`);
|
|
638
|
+
throw new telemetry_1.CodedError('NeedAutolinking', `Auto-linking changes were necessary but --check was specified. Run '${autolinkCommand}' to apply the changes`);
|
|
639
|
+
}
|
|
640
|
+
else {
|
|
641
|
+
console.log(`${chalk_1.default.green('Success:')} Auto-linking changes completed. (${Math.round(endTime - startTime)}ms)`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
catch (e) {
|
|
645
|
+
spinner.fail();
|
|
646
|
+
const endTime = perf_hooks_1.performance.now();
|
|
647
|
+
console.log(`${chalk_1.default.red('Error:')} ${e.toString()}. (${Math.round(endTime - startTime)}ms)`);
|
|
648
|
+
throw e;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
exports.autolinkWindowsInternal = autolinkWindowsInternal;
|
|
652
|
+
/**
|
|
653
|
+
* Performs auto-linking for RNW native modules and apps.
|
|
654
|
+
*/
|
|
655
|
+
exports.autolinkCommand = {
|
|
656
|
+
name: 'autolink-windows',
|
|
657
|
+
description: 'Runs Windows-specific autolinking',
|
|
658
|
+
func: autolinkWindows,
|
|
659
|
+
options: autolinkWindowsOptions_1.autolinkOptions,
|
|
660
|
+
};
|
|
661
661
|
//# sourceMappingURL=autolinkWindows.js.map
|