我在使用tfx-cli打包Azure Devops插件时,输出了很黄很黄很亮瞎眼的(尤其是在Visual Studio Code采用了Dark Black Theme的情况下)警告warning:

PS D:\Works\boards-extensions> npm run package
> vsts-widgets@1.0.234 package D:\Works\boards-extensions
tfx extension create --manifest-globs vss-extension.json TFS Cross Platform Command Line Interface v0.7.9
Copyright Microsoft Corporation
warning: Could not determine content type for extension .woff2. Defaulting to application/octet-stream. To override this, add a contentType property to this file entry in the manifest.
warning: Could not determine content type for extension .ttf. Defaulting to application/octet-stream. To override this, add a contentType property to this file entry in the manifest.
warning: Could not determine content type for extension .otf. Defaulting to application/octet-stream. To override this, add a contentType property to this file entry in the manifest.

这让我有股消灭警告的冲动,

于是经过一番google后,我找到了一段打包代码,如下:

/**
* Generates the required [Content_Types].xml file for the vsix package.
* This xml contains a <Default> entry for each different file extension
* found in the package, mapping it to the appropriate MIME type.
*/
private genContentTypesXml(fileNames: string[], overrides: {[partName: string]: PackagePart}): Q.Promise<string> {
log.debug("Generating [Content_Types].xml");
let contentTypes: any = {
Types: {
$: {
xmlns: "http://schemas.openxmlformats.org/package/2006/content-types"
},
Default: [],
Override: []
}
};
let windows = /^win/.test(process.platform);
let contentTypePromise;
if (windows) {
// On windows, check HKCR to get the content type of the file based on the extension
let contentTypePromises: Q.Promise<any>[] = [];
let extensionlessFiles = [];
let uniqueExtensions = _.unique<string>(fileNames.map((f) => {
let extName = path.extname(f);
if (!extName && !overrides[f]) {
log.warn("File %s does not have an extension, and its content-type is not declared. Defaulting to application/octet-stream.", path.resolve(f));
overrides[f] = {partName: f, contentType: "application/octet-stream"};
}
if (overrides[f]) {
// If there is an override for this file, ignore its extension
return "";
}
return extName;
}));
uniqueExtensions.forEach((ext) => {
if (!ext.trim()) {
return;
}
if (!ext) {
return;
}
if (VsixWriter.CONTENT_TYPE_MAP[ext.toLowerCase()]) {
contentTypes.Types.Default.push({
$: {
Extension: ext,
ContentType: VsixWriter.CONTENT_TYPE_MAP[ext.toLowerCase()]
}
});
return;
}
let hkcrKey = new winreg({
hive: winreg.HKCR,
key: "\\" + ext.toLowerCase()
});
let regPromise = Q.ninvoke(hkcrKey, "get", "Content Type").then((type: WinregValue) => {
log.debug("Found content type for %s: %s.", ext, type.value);
let contentType = "application/octet-stream";
if (type) {
contentType = type.value;
}
return contentType;
}).catch((err) => {
log.warn("Could not determine content type for extension %s. Defaulting to application/octet-stream. To override this, add a contentType property to this file entry in the manifest.", ext);
return "application/octet-stream";
}).then((contentType) => {
contentTypes.Types.Default.push({
$: {
Extension: ext,
ContentType: contentType
}
});
});
contentTypePromises.push(regPromise);
});
contentTypePromise = Q.all(contentTypePromises);
} else {
// If not on windows, run the file --mime-type command to use magic to get the content type.
// If the file has an extension, rev a hit counter for that extension and the extension
// If there is no extension, create an <Override> element for the element
// For each file with an extension that doesn't match the most common type for that extension
// (tracked by the hit counter), create an <Override> element.
// Finally, add a <Default> element for each extension mapped to the most common type. let contentTypePromises: Q.Promise<any>[] = [];
let extTypeCounter: {[ext: string]: {[type: string]: string[]}} = {};
fileNames.forEach((fileName) => {
let extension = path.extname(fileName);
let mimePromise;
if (VsixWriter.CONTENT_TYPE_MAP[extension]) {
if (!extTypeCounter[extension]) {
extTypeCounter[extension] = {};
}
if (!extTypeCounter[extension][VsixWriter.CONTENT_TYPE_MAP[extension]]) {
extTypeCounter[extension][VsixWriter.CONTENT_TYPE_MAP[extension]] = [];
}
extTypeCounter[extension][VsixWriter.CONTENT_TYPE_MAP[extension]].push(fileName);
mimePromise = Q.resolve(null);
return;
}
mimePromise = Q.Promise((resolve, reject, notify) => {
let child = childProcess.exec("file --mime-type \"" + fileName + "\"", (err, stdout, stderr) => {
try {
if (err) {
reject(err);
}
let stdoutStr = stdout.toString("utf8");
let magicMime = _.trimRight(stdoutStr.substr(stdoutStr.lastIndexOf(" ") + 1), "\n");
log.debug("Magic mime type for %s is %s.", fileName, magicMime);
if (magicMime) {
if (extension) {
if (!extTypeCounter[extension]) {
extTypeCounter[extension] = {};
}
let hitCounters = extTypeCounter[extension];
if (!hitCounters[magicMime]) {
hitCounters[magicMime] = [];
}
hitCounters[magicMime].push(fileName);
} else {
if (!overrides[fileName]) {
overrides[fileName].contentType = magicMime;
}
}
} else {
if (stderr) {
reject(stderr.toString("utf8"));
} else {
log.warn("Could not determine content type for %s. Defaulting to application/octet-stream. To override this, add a contentType property to this file entry in the manifest.", fileName);
overrides[fileName].contentType = "application/octet-stream";
}
}
resolve(null);
} catch (e) {
reject(e);
}
});
});
contentTypePromises.push(mimePromise);
});
contentTypePromise = Q.all(contentTypePromises).then(() => {
Object.keys(extTypeCounter).forEach((ext) => {
let hitCounts = extTypeCounter[ext];
let bestMatch = this.maxKey<string[]>(hitCounts, (i => i.length));
Object.keys(hitCounts).forEach((type) => {
if (type === bestMatch) {
return;
}
hitCounts[type].forEach((fileName) => {
overrides[fileName].contentType = type;
});
});
contentTypes.Types.Default.push({
$: {
Extension: ext,
ContentType: bestMatch
}
});
});
});
}
return contentTypePromise.then(() => {
Object.keys(overrides).forEach((partName) => {
contentTypes.Types.Override.push({
$: {
ContentType: overrides[partName].contentType,
PartName: "/" + _.trimLeft(partName, "/")
}
})
});
let builder = new xml.Builder(VsixWriter.DEFAULT_XML_BUILDER_SETTINGS);
return builder.buildObject(contentTypes).replace(/\n/g, os.EOL);
});
}

其中有一小段很有意思的,它尝试从当前电脑的注册表中获取扩展名对应的Content Type值,用以填充它的[Content_Types].xml

let hkcrKey = new winreg({
hive: winreg.HKCR,
key: "\\" + ext.toLowerCase()
});
let regPromise = Q.ninvoke(hkcrKey, "get", "Content Type").then((type: WinregValue) => {
log.debug("Found content type for %s: %s.", ext, type.value);
let contentType = "application/octet-stream";
if (type) {
contentType = type.value;
}
return contentType;
})

根据这段代码我找到了注册表的以下项:

  • HKEY_CLASSES_ROOT\.otf
  • HKEY_CLASSES_ROOT\.woff2
  • HKEY_CLASSES_ROOT\.ttf

这些项里均没有Content Type键值,所以我写了个reg文件进行注册:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\.otf]
"Content Type"="application/x-font-opentype"
[HKEY_CLASSES_ROOT\.ttf]
"Content Type"="application/x-font-truetype"
[HKEY_CLASSES_ROOT\.woff2]
"Content Type"="application/x-font-woff2"

重新打包,警告不再出现了....

解决前的[Content_types].xml

<?xml version="1.0" encoding="utf-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension=".html" ContentType="text/html"/>
<Default Extension=".js" ContentType="application/javascript"/>
<Default Extension=".ts" ContentType="text/plain"/>
<Default Extension=".css" ContentType="text/css"/>
<Default Extension=".png" ContentType="image/png"/>
<Default Extension=".eot" ContentType="application/vnd.ms-fontobject"/>
<Default Extension=".svg" ContentType="image/svg+xml"/>
<Default Extension=".woff" ContentType="application/font-woff"/>
<Default Extension=".map" ContentType="application/json"/>
<Default Extension=".woff2" ContentType="application/octet-stream"/>
<Default Extension=".ttf" ContentType="application/octet-stream"/>
<Default Extension=".otf" ContentType="application/octet-stream"/>
<Default Extension=".vsixmanifest" ContentType="text/xml"/>
<Default Extension=".vsomanifest" ContentType="application/json"/>
<Override ContentType="application/x-font-woff2" PartName="/node_modules/font-awesome/fonts/fontawesome-webfont.woff2"/>
<Override ContentType="application/x-font-truetype" PartName="/node_modules/font-awesome/fonts/fontawesome-webfont.ttf"/>
<Override ContentType="application/x-font-opentype" PartName="/node_modules/font-awesome/fonts/FontAwesome.otf"/>
</Types>

解决后的[Content_types].xml

<?xml version="1.0" encoding="utf-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension=".html" ContentType="text/html"/>
<Default Extension=".js" ContentType="application/javascript"/>
<Default Extension=".ts" ContentType="text/plain"/>
<Default Extension=".css" ContentType="text/css"/>
<Default Extension=".png" ContentType="image/png"/>
<Default Extension=".eot" ContentType="application/vnd.ms-fontobject"/>
<Default Extension=".svg" ContentType="image/svg+xml"/>
<Default Extension=".woff" ContentType="application/font-woff"/>
<Default Extension=".map" ContentType="application/json"/>
<Default Extension=".woff2" ContentType="application/x-font-woff2"/>
<Default Extension=".ttf" ContentType="application/x-font-truetype"/>
<Default Extension=".otf" ContentType="application/x-font-opentype"/>
<Default Extension=".vsixmanifest" ContentType="text/xml"/>
<Default Extension=".vsomanifest" ContentType="application/json"/>
<Override ContentType="application/x-font-woff2" PartName="/node_modules/font-awesome/fonts/fontawesome-webfont.woff2"/>
<Override ContentType="application/x-font-truetype" PartName="/node_modules/font-awesome/fonts/fontawesome-webfont.ttf"/>
<Override ContentType="application/x-font-opentype" PartName="/node_modules/font-awesome/fonts/FontAwesome.otf"/>
</Types>

Azure Devops (VSTS) Extensions 开发小记的更多相关文章

  1. Azure DevOps(一)利用Azure DevOps Pipeline 构建应用程序镜像到AWS ECR

    一,引言 最近项目上让开始学习AWS,作为一名合格的开发人员,当然也是学会利用Azure DevOps Pipeline 将应用程序部署到 AWS ECS(完全托管的容器编排服务).我们要学会将应用程 ...

  2. 微软改名部又出动啦!微软宣布VSTS改名为Azure DevOps

    本篇为翻译,原文地址:https://azure.microsoft.com/en-us/blog/introducing-azure-devops/ 作者:Jamie Cool,Azure DevO ...

  3. VSTS 更名为 Azure DevOps

    微软正式对外宣布Azure DevOps,其实就是原来的VSTS,我们来看一下Azure DevOps的介绍: 今天我们宣布Azure DevOps.与世界各地的客户和开发人员合作,很明显,DevOp ...

  4. [转贴]infoQ VSTS被拆成5个部分,以Azure DevOps服务形式推出

    VSTS被拆成5个部分,以Azure DevOps服务形式推出 http://www.infoq.com/cn/news/2018/09/vsts-divide5parts-azuredevops?u ...

  5. 使用ML.NET + Azure DevOps + Azure Container Instances打造机器学习生产化

    介绍 Azure DevOps,以前称为Visual Studio Team Services(VSTS),可帮助个人和组织更快地规划,协作和发布产品.其中一项值得注意的服务是Azure Pipeli ...

  6. 你好,Azure DevOps Server 2019;再见,Team Foundation Server

    微软正式发布Azure DevOps Server 2019的第一个版本,作为Team Foundation Server (TFS)2018的升级版本和替代产品. 这是目前市面上唯一一款将产品名称冠 ...

  7. Azure DevOps Server(TFS) 客户端分析

    Azure DevOps Server(TFS) 是微软公司的软件协作开发管理平台产品,为软件研发.测试.实施提供全流程的服务.作为一款应用服务器产品,他的客户端是什么,在哪里下载客户端?我们在项目实 ...

  8. Azure Devops测试管理(上)

    因为最近测试人员合并到我这边开发组,对于如何能更好管理测试流程和测试与开发能更高效的完成任务,通俗的说如何能更敏捷,深入思考,然后就开始琢磨起TFS(也称之为VSTS/Azure Devops,因为我 ...

  9. 【Azure DevOps系列】什么是Azure DevOps

    DevOps DevOps是一种重视"软件开发人员(Dev)"和"IT运维技术人员(Ops)"之间沟通合作的文化,它促进开发和运营团队之间的协作,以自动化和可重 ...

随机推荐

  1. Knative 基本功能深入剖析:Knative Serving 自动扩缩容 Autoscaler

    Knative Serving 默认情况下,提供了开箱即用的快速.基于请求的自动扩缩容功能 - Knative Pod Autoscaler(KPA).下面带你体验如何在 Knative 中玩转 Au ...

  2. Windows Server 2008 R2 install Visual Studio 2015 failed

    Please download and install Windows Server 2008 R2 Service Pack 1 (KB976932) . https://www.microsoft ...

  3. SqlServer 创建数据库两种方式

    一个SqlServer 数据库实例大概可以创建三万多个数据库. 创建数据库的第一种方式:SqlServer Management Studio管理工具进行可视化创建. 1).打开数据库管理工具,在&q ...

  4. "初识".Net Winfom

    对于“初识”Winform中 初识这两个字的涵义,实际上之前我一直接触的是B/S方面的知识和开发,虽然说不上是熟练,但是大部分时间都是花在B/S上了,例如MVC,如今要从B/S转到C/S了,说实话心里 ...

  5. mssql SQL Server 2008 阻止保存要求重新创建表的更改问题的设置方法

    解决方法: 工具-〉选项-〉左侧有个 设计器-〉表设计器和数据库设计器 -> 阻止保存要求重新创建表的更改(右侧) 把钩去掉即可.

  6. webpack4 打包静态资源

    demo 代码点此,开始之前,先做点准备工作. 准备工作 准备一个空文件夹,然后执行下列命令: npm init -y npm i -D webpack webpack-cli 然后创建一个 dist ...

  7. 4. 移动安全渗透测试-(Android逆向基础)

    4.1 smali 基础 1.注释 smali中使用#来代表注释一行例如:# const-string v0, "aaa" #这句不会被执行 2.数据类型 V void,只能用于返 ...

  8. Android培训准备资料之UI一些相似控件和控件一些相似属性之间的区别

    这一篇博客主要收集五大布局中的一些相似控件和控件一些相似属性之间的区别 ImageView ImageButton Button 三者有啥区别? (1)Button继承自TextView,ImageV ...

  9. Django Windows+IIS+wfastcgi 环境下部署

    教程基于 Windows 10专业版 + Python3.6 + IIS + wfastcgi 之上部署Django2.2的,同样适用于Windows server2012服务器和Windows7及以 ...

  10. 201871010104-陈园园 《面向对象程序设计(java)》第十四周学习总结

    201871010104-陈园园 <面向对象程序设计(java)>第十四周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ ...