pcf-scripts
Version:
This package contains a module for building PowerApps Component Framework (PCF) controls. See project homepage how to install.
155 lines (153 loc) • 7.92 kB
JavaScript
"use strict";
// Copyright (C) Microsoft Corporation. All rights reserved.
Object.defineProperty(exports, "__esModule", { value: true });
exports.ManifestProcessor = void 0;
const lodash_1 = require("lodash");
const path = require("node:path");
const constants = require("./constants");
const controlManifest_1 = require("./controlManifest");
const diagnosticMessages_generated_1 = require("./diagnosticMessages.generated");
const featureManager_1 = require("./featureManager");
const PackageVersionUtils_1 = require("./generated/PackageVersionUtils");
class ManifestProcessor {
constructor(manifestData, controlPath, diag) {
this.resourcesMap = {};
this.nameTracker = {};
this.processedManifest = (0, lodash_1.cloneDeep)(manifestData);
this.controlAbsPath = path.resolve(controlPath);
this.controlRoot = path.dirname(this.controlAbsPath);
this.diag = diag;
this.nameTracker[constants.BUNDLE_NAME] = 1;
this.featureManager = new featureManager_1.FeatureManager();
}
// rename paths in <code> and <library>.<packaged_library> under <resources>
// add built-by element as a child of control node; stamp PCF API version used
getProcessedManifest() {
this.addBuiltBy();
this.stampApiVersion();
this.addSubscribedFunctionalities();
this.processResources();
return {
processedManifest: new controlManifest_1.ControlManifest(this.processedManifest),
resourcesMap: this.resourcesMap,
};
}
addSubscribedFunctionalities() {
const properties = this.processedManifest.manifest.control.property;
if (properties) {
if (properties.some((prop) => prop.$["of-type"] === "Object")) {
this.addSubscribedFunctionality(constants.SF_SHARED_TEMPLATE, false);
}
}
}
addSubscribedFunctionality(functionality, isSubscribed) {
let subFuncParentNode = this.processedManifest.manifest.control[constants.SUBSCRIBED_FUNCTIONALITY_SECTION_NODE];
subFuncParentNode ?? (subFuncParentNode = [
{
"subscribed-functionality": [],
},
]);
if (subFuncParentNode[0]["subscribed-functionality"].filter((el) => el.$.name === functionality).length === 0) {
subFuncParentNode[0]["subscribed-functionality"].push({
$: {
name: functionality,
value: isSubscribed.toString(),
},
});
}
this.processedManifest.manifest.control[constants.SUBSCRIBED_FUNCTIONALITY_SECTION_NODE] = subFuncParentNode;
}
// rename paths specified by preview-image attribute as well as <code> and <library>.<packaged_library> under <resources>
processResources() {
const resources = this.processedManifest.manifest.control.resources;
const previewImagePath = this.processedManifest.manifest.control.$["preview-image"];
if (previewImagePath) {
this.processedManifest.manifest.control.$["preview-image"] = this.checkResourcePath(previewImagePath);
}
Object.entries(resources)
.filter(([resourceType]) => resourceType !== constants.PLATFORM_LIBRARY_ELEM_NAME && resourceType !== constants.RES_DEPENDENCY_ELEM_NAME)
.forEach(([resourceType, resourceList]) => {
if (resourceType === constants.CODE_ELEM_NAME) {
// the xsd allows more than one <code> element, but our opinionated build will validate
// there is only one <code> element
resourceList[0].$.path = constants.BUNDLE_NAME;
}
else if (resourceType === constants.LIBRARY_ELEM_NAME) {
resourceList.forEach((lib) => {
const packagedLibs = lib.packaged_library;
if (packagedLibs) {
packagedLibs.forEach((packagedLib) => {
packagedLib.$.path = this.checkResourcePath(packagedLib.$.path, packagedLib.$.outputDirectory);
});
}
});
}
else {
resourceList.forEach((resource) => {
resource.$.path = this.checkResourcePath(resource.$.path, resource.$.outputDirectory);
});
}
});
}
// add built-by element as a child of control node
addBuiltBy() {
const buildToolVersion = (0, PackageVersionUtils_1.getPackageVersion)(constants.PCF_SCRIPTS_PACKAGE_NAME, this.controlRoot);
if (!buildToolVersion) {
this.diag.pushA(diagnosticMessages_generated_1.strings.package_version_not_found, [constants.PCF_SCRIPTS_PACKAGE_NAME]);
return;
}
this.processedManifest.manifest.control["built-by"] = {
$: {
name: constants.BUILD_TOOL_NAME,
version: buildToolVersion,
},
};
}
// obtain the version of the ComponentFramework API from the local package-lock.json and stamp it in CM.xml
// optional parameter for unit test; return a boolean that indicates whether version info is found
stampApiVersion() {
const requireInternalTypings = this.featureManager.isFeatureEnabled("pcfUseInternalTypes");
const pcfPackage = requireInternalTypings ? constants.COMPONENT_FRAMEWORK_INTERNAL_PACKAGE_NAME : constants.COMPONENT_FRAMEWORK_PACKAGE_NAME;
const apiVersion = (0, PackageVersionUtils_1.getPackageVersion)(pcfPackage, this.controlRoot);
if (!apiVersion) {
this.diag.pushA(diagnosticMessages_generated_1.strings.package_version_not_found, [constants.COMPONENT_FRAMEWORK_PACKAGE_NAME]);
return;
}
this.processedManifest.manifest.control.$["api-version"] = apiVersion;
}
// First check whether the resource's relative path points to a file within the control folder.
// If so, preserve the folder structure in outdir. If not, check whether a resource's file name collides
// with any other resources file. If so returns a new file name otherwise return original name.
checkResourcePath(resourceRelativePath, outputDirectory) {
const resourceName = path.basename(resourceRelativePath);
const resourceAbsPath = path.join(this.controlAbsPath, resourceRelativePath);
const simplifiedRelativePath = path.relative(this.controlAbsPath, resourceAbsPath);
let relativePathInOutDir;
if (!simplifiedRelativePath.startsWith("..") && simplifiedRelativePath.length > 0) {
relativePathInOutDir = simplifiedRelativePath;
}
else {
relativePathInOutDir = resourceName;
}
// convert windows format to the path format in manifest
relativePathInOutDir = outputDirectory
? path.join(outputDirectory, path.basename(relativePathInOutDir)).replace(/\\/g, "/")
: relativePathInOutDir.replace(/\\/g, "/");
if (this.nameTracker[relativePathInOutDir]) {
const ext = path.extname(relativePathInOutDir);
const suffix = this.nameTracker[relativePathInOutDir];
// increment a number as suffix to differentiate files with existing names
const newResourcePath = `${relativePathInOutDir.substring(0, relativePathInOutDir.length - ext.length)}${suffix}${ext}`;
this.nameTracker[relativePathInOutDir]++;
this.resourcesMap[resourceRelativePath] = newResourcePath;
return newResourcePath;
}
else {
this.nameTracker[relativePathInOutDir] = 1;
this.resourcesMap[resourceRelativePath] = relativePathInOutDir;
return relativePathInOutDir;
}
}
}
exports.ManifestProcessor = ManifestProcessor;
//# sourceMappingURL=manifestProcessor.js.map