mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Merge from master
This commit is contained in:
@@ -3,21 +3,19 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { join, normalize, extname } from 'path';
|
||||
import * as json from 'vs/base/common/json';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator';
|
||||
import * as path from 'path';
|
||||
import * as semver from 'semver';
|
||||
import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
|
||||
import * as json from 'vs/base/common/json';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages';
|
||||
import { groupByExtension, getGalleryExtensionId, getLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { getGalleryExtensionId, getLocalExtensionId, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
|
||||
import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator';
|
||||
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
||||
const MANIFEST_FILE = 'package.json';
|
||||
|
||||
@@ -51,7 +49,7 @@ namespace Translations {
|
||||
|
||||
export interface NlsConfiguration {
|
||||
readonly devMode: boolean;
|
||||
readonly locale: string;
|
||||
readonly locale: string | undefined;
|
||||
readonly pseudo: boolean;
|
||||
readonly translations: Translations;
|
||||
}
|
||||
@@ -77,13 +75,13 @@ abstract class ExtensionManifestHandler {
|
||||
this._absoluteFolderPath = absoluteFolderPath;
|
||||
this._isBuiltin = isBuiltin;
|
||||
this._isUnderDevelopment = isUnderDevelopment;
|
||||
this._absoluteManifestPath = join(absoluteFolderPath, MANIFEST_FILE);
|
||||
this._absoluteManifestPath = path.join(absoluteFolderPath, MANIFEST_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
class ExtensionManifestParser extends ExtensionManifestHandler {
|
||||
|
||||
public parse(): TPromise<IExtensionDescription> {
|
||||
public parse(): Promise<IExtensionDescription> {
|
||||
return pfs.readFile(this._absoluteManifestPath).then((manifestContents) => {
|
||||
try {
|
||||
const manifest = JSON.parse(manifestContents.toString());
|
||||
@@ -120,7 +118,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
|
||||
this._nlsConfig = nlsConfig;
|
||||
}
|
||||
|
||||
public replaceNLS(extensionDescription: IExtensionDescription): TPromise<IExtensionDescription> {
|
||||
public replaceNLS(extensionDescription: IExtensionDescription): Promise<IExtensionDescription> {
|
||||
interface MessageBag {
|
||||
[key: string]: string;
|
||||
}
|
||||
@@ -132,22 +130,22 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
|
||||
}
|
||||
|
||||
interface LocalizedMessages {
|
||||
values: MessageBag;
|
||||
default: string;
|
||||
values: MessageBag | undefined;
|
||||
default: string | null;
|
||||
}
|
||||
|
||||
const reportErrors = (localized: string, errors: json.ParseError[]): void => {
|
||||
const reportErrors = (localized: string | null, errors: json.ParseError[]): void => {
|
||||
errors.forEach((error) => {
|
||||
this._log.error(this._absoluteFolderPath, nls.localize('jsonsParseReportErrors', "Failed to parse {0}: {1}.", localized, getParseErrorMessage(error.error)));
|
||||
});
|
||||
};
|
||||
|
||||
let extension = extname(this._absoluteManifestPath);
|
||||
let extension = path.extname(this._absoluteManifestPath);
|
||||
let basename = this._absoluteManifestPath.substr(0, this._absoluteManifestPath.length - extension.length);
|
||||
|
||||
const translationId = `${extensionDescription.publisher}.${extensionDescription.name}`;
|
||||
let translationPath = this._nlsConfig.translations[translationId];
|
||||
let localizedMessages: TPromise<LocalizedMessages>;
|
||||
let localizedMessages: Promise<LocalizedMessages | undefined>;
|
||||
if (translationPath) {
|
||||
localizedMessages = pfs.readFile(translationPath, 'utf8').then<LocalizedMessages, LocalizedMessages>((content) => {
|
||||
let errors: json.ParseError[] = [];
|
||||
@@ -163,7 +161,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
|
||||
return { values: undefined, default: `${basename}.nls.json` };
|
||||
});
|
||||
} else {
|
||||
localizedMessages = pfs.fileExists(basename + '.nls' + extension).then<LocalizedMessages, undefined | LocalizedMessages>(exists => {
|
||||
localizedMessages = pfs.fileExists(basename + '.nls' + extension).then<LocalizedMessages | undefined, LocalizedMessages | undefined>(exists => {
|
||||
if (!exists) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -211,8 +209,8 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
|
||||
/**
|
||||
* Parses original message bundle, returns null if the original message bundle is null.
|
||||
*/
|
||||
private static resolveOriginalMessageBundle(originalMessageBundle: string, errors: json.ParseError[]) {
|
||||
return new TPromise<{ [key: string]: string; }>((c, e) => {
|
||||
private static resolveOriginalMessageBundle(originalMessageBundle: string | null, errors: json.ParseError[]) {
|
||||
return new Promise<{ [key: string]: string; } | null>((c, e) => {
|
||||
if (originalMessageBundle) {
|
||||
pfs.readFile(originalMessageBundle).then(originalBundleContent => {
|
||||
c(json.parse(originalBundleContent.toString(), errors));
|
||||
@@ -229,8 +227,8 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
|
||||
* Finds localized message bundle and the original (unlocalized) one.
|
||||
* If the localized file is not present, returns null for the original and marks original as localized.
|
||||
*/
|
||||
private static findMessageBundles(nlsConfig: NlsConfiguration, basename: string): TPromise<{ localized: string, original: string }> {
|
||||
return new TPromise<{ localized: string, original: string }>((c, e) => {
|
||||
private static findMessageBundles(nlsConfig: NlsConfiguration, basename: string): Promise<{ localized: string; original: string | null; }> {
|
||||
return new Promise<{ localized: string; original: string | null; }>((c, e) => {
|
||||
function loop(basename: string, locale: string): void {
|
||||
let toCheck = `${basename}.nls.${locale}.json`;
|
||||
pfs.fileExists(toCheck).then(exists => {
|
||||
@@ -258,7 +256,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
|
||||
* This routine makes the following assumptions:
|
||||
* The root element is an object literal
|
||||
*/
|
||||
private static _replaceNLStrings<T>(nlsConfig: NlsConfiguration, literal: T, messages: { [key: string]: string; }, originalMessages: { [key: string]: string }, log: ILog, messageScope: string): void {
|
||||
private static _replaceNLStrings<T>(nlsConfig: NlsConfiguration, literal: T, messages: { [key: string]: string; }, originalMessages: { [key: string]: string } | null, log: ILog, messageScope: string): void {
|
||||
function processEntry(obj: any, key: string | number, command?: boolean) {
|
||||
let value = obj[key];
|
||||
if (types.isString(value)) {
|
||||
@@ -320,7 +318,7 @@ export interface IRelaxedExtensionDescription {
|
||||
}
|
||||
|
||||
class ExtensionManifestValidator extends ExtensionManifestHandler {
|
||||
validate(_extensionDescription: IExtensionDescription): IExtensionDescription {
|
||||
validate(_extensionDescription: IExtensionDescription): IExtensionDescription | null {
|
||||
let extensionDescription = <IRelaxedExtensionDescription>_extensionDescription;
|
||||
extensionDescription.isBuiltin = this._isBuiltin;
|
||||
extensionDescription.isUnderDevelopment = this._isUnderDevelopment;
|
||||
@@ -338,12 +336,17 @@ class ExtensionManifestValidator extends ExtensionManifestHandler {
|
||||
this._log.warn(this._absoluteFolderPath, error);
|
||||
});
|
||||
|
||||
// allow publisher to be undefined to make the initial extension authoring experience smoother
|
||||
if (!extensionDescription.publisher) {
|
||||
extensionDescription.publisher = 'undefined_publisher';
|
||||
}
|
||||
|
||||
// id := `publisher.name`
|
||||
extensionDescription.id = `${extensionDescription.publisher}.${extensionDescription.name}`;
|
||||
|
||||
// main := absolutePath(`main`)
|
||||
if (extensionDescription.main) {
|
||||
extensionDescription.main = join(this._absoluteFolderPath, extensionDescription.main);
|
||||
extensionDescription.main = path.join(this._absoluteFolderPath, extensionDescription.main);
|
||||
}
|
||||
|
||||
extensionDescription.extensionLocation = URI.file(this._absoluteFolderPath);
|
||||
@@ -370,8 +373,8 @@ class ExtensionManifestValidator extends ExtensionManifestHandler {
|
||||
notices.push(nls.localize('extensionDescription.empty', "Got empty extension description"));
|
||||
return false;
|
||||
}
|
||||
if (typeof extensionDescription.publisher !== 'string') {
|
||||
notices.push(nls.localize('extensionDescription.publisher', "property `{0}` is mandatory and must be of type `string`", 'publisher'));
|
||||
if (typeof extensionDescription.publisher !== 'undefined' && typeof extensionDescription.publisher !== 'string') {
|
||||
notices.push(nls.localize('extensionDescription.publisher', "property publisher must be of type `string`."));
|
||||
return false;
|
||||
}
|
||||
if (typeof extensionDescription.name !== 'string') {
|
||||
@@ -411,7 +414,7 @@ class ExtensionManifestValidator extends ExtensionManifestHandler {
|
||||
notices.push(nls.localize('extensionDescription.main1', "property `{0}` can be omitted or must be of type `string`", 'main'));
|
||||
return false;
|
||||
} else {
|
||||
let normalizedAbsolutePath = join(extensionFolderPath, extensionDescription.main);
|
||||
let normalizedAbsolutePath = path.join(extensionFolderPath, extensionDescription.main);
|
||||
|
||||
if (normalizedAbsolutePath.indexOf(extensionFolderPath)) {
|
||||
notices.push(nls.localize('extensionDescription.main2', "Expected `main` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", normalizedAbsolutePath, extensionFolderPath));
|
||||
@@ -445,8 +448,8 @@ export class ExtensionScannerInput {
|
||||
|
||||
constructor(
|
||||
public readonly ourVersion: string,
|
||||
public readonly commit: string,
|
||||
public readonly locale: string,
|
||||
public readonly commit: string | undefined,
|
||||
public readonly locale: string | undefined,
|
||||
public readonly devMode: boolean,
|
||||
public readonly absoluteFolderPath: string,
|
||||
public readonly isBuiltin: boolean,
|
||||
@@ -486,16 +489,16 @@ export interface IExtensionReference {
|
||||
}
|
||||
|
||||
export interface IExtensionResolver {
|
||||
resolveExtensions(): TPromise<IExtensionReference[]>;
|
||||
resolveExtensions(): Promise<IExtensionReference[]>;
|
||||
}
|
||||
|
||||
class DefaultExtensionResolver implements IExtensionResolver {
|
||||
|
||||
constructor(private root: string) { }
|
||||
|
||||
resolveExtensions(): TPromise<IExtensionReference[]> {
|
||||
resolveExtensions(): Promise<IExtensionReference[]> {
|
||||
return pfs.readDirsInDir(this.root)
|
||||
.then(folders => folders.map(name => ({ name, path: join(this.root, name) })));
|
||||
.then(folders => folders.map(name => ({ name, path: path.join(this.root, name) })));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,11 +507,11 @@ export class ExtensionScanner {
|
||||
/**
|
||||
* Read the extension defined in `absoluteFolderPath`
|
||||
*/
|
||||
public static scanExtension(version: string, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, isUnderDevelopment: boolean, nlsConfig: NlsConfiguration): TPromise<IExtensionDescription> {
|
||||
absoluteFolderPath = normalize(absoluteFolderPath);
|
||||
public static scanExtension(version: string, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, isUnderDevelopment: boolean, nlsConfig: NlsConfiguration): Promise<IExtensionDescription | null> {
|
||||
absoluteFolderPath = path.normalize(absoluteFolderPath);
|
||||
|
||||
let parser = new ExtensionManifestParser(version, log, absoluteFolderPath, isBuiltin, isUnderDevelopment);
|
||||
return parser.parse().then((extensionDescription) => {
|
||||
return parser.parse().then<IExtensionDescription | null>((extensionDescription) => {
|
||||
if (extensionDescription === null) {
|
||||
return null;
|
||||
}
|
||||
@@ -528,7 +531,7 @@ export class ExtensionScanner {
|
||||
/**
|
||||
* Scan a list of extensions defined in `absoluteFolderPath`
|
||||
*/
|
||||
public static async scanExtensions(input: ExtensionScannerInput, log: ILog, resolver: IExtensionResolver = null): Promise<IExtensionDescription[]> {
|
||||
public static async scanExtensions(input: ExtensionScannerInput, log: ILog, resolver: IExtensionResolver | null = null): Promise<IExtensionDescription[]> {
|
||||
const absoluteFolderPath = input.absoluteFolderPath;
|
||||
const isBuiltin = input.isBuiltin;
|
||||
const isUnderDevelopment = input.isUnderDevelopment;
|
||||
@@ -541,7 +544,7 @@ export class ExtensionScanner {
|
||||
let obsolete: { [folderName: string]: boolean; } = {};
|
||||
if (!isBuiltin) {
|
||||
try {
|
||||
const obsoleteFileContents = await pfs.readFile(join(absoluteFolderPath, '.obsolete'), 'utf8');
|
||||
const obsoleteFileContents = await pfs.readFile(path.join(absoluteFolderPath, '.obsolete'), 'utf8');
|
||||
obsolete = JSON.parse(obsoleteFileContents);
|
||||
} catch (err) {
|
||||
// Don't care
|
||||
@@ -572,7 +575,8 @@ export class ExtensionScanner {
|
||||
}
|
||||
|
||||
const nlsConfig = ExtensionScannerInput.createNLSConfig(input);
|
||||
let extensionDescriptions = await TPromise.join(refs.map(r => this.scanExtension(input.ourVersion, log, r.path, isBuiltin, isUnderDevelopment, nlsConfig)));
|
||||
let _extensionDescriptions = await Promise.all(refs.map(r => this.scanExtension(input.ourVersion, log, r.path, isBuiltin, isUnderDevelopment, nlsConfig)));
|
||||
let extensionDescriptions = arrays.coalesce(_extensionDescriptions);
|
||||
extensionDescriptions = extensionDescriptions.filter(item => item !== null && !obsolete[getLocalExtensionId(getGalleryExtensionId(item.publisher, item.name), item.version)]);
|
||||
|
||||
if (!isBuiltin) {
|
||||
@@ -598,12 +602,12 @@ export class ExtensionScanner {
|
||||
* Combination of scanExtension and scanExtensions: If an extension manifest is found at root, we load just this extension,
|
||||
* otherwise we assume the folder contains multiple extensions.
|
||||
*/
|
||||
public static scanOneOrMultipleExtensions(input: ExtensionScannerInput, log: ILog): TPromise<IExtensionDescription[]> {
|
||||
public static scanOneOrMultipleExtensions(input: ExtensionScannerInput, log: ILog): Promise<IExtensionDescription[]> {
|
||||
const absoluteFolderPath = input.absoluteFolderPath;
|
||||
const isBuiltin = input.isBuiltin;
|
||||
const isUnderDevelopment = input.isUnderDevelopment;
|
||||
|
||||
return pfs.fileExists(join(absoluteFolderPath, MANIFEST_FILE)).then((exists) => {
|
||||
return pfs.fileExists(path.join(absoluteFolderPath, MANIFEST_FILE)).then((exists) => {
|
||||
if (exists) {
|
||||
const nlsConfig = ExtensionScannerInput.createNLSConfig(input);
|
||||
return this.scanExtension(input.ourVersion, log, absoluteFolderPath, isBuiltin, isUnderDevelopment, nlsConfig).then((extensionDescription) => {
|
||||
@@ -619,4 +623,31 @@ export class ExtensionScanner {
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
public static mergeBuiltinExtensions(builtinExtensions: Promise<IExtensionDescription[]>, extraBuiltinExtensions: Promise<IExtensionDescription[]>): Promise<IExtensionDescription[]> {
|
||||
return Promise.all([builtinExtensions, extraBuiltinExtensions]).then(([builtinExtensions, extraBuiltinExtensions]) => {
|
||||
let resultMap: { [id: string]: IExtensionDescription; } = Object.create(null);
|
||||
for (let i = 0, len = builtinExtensions.length; i < len; i++) {
|
||||
resultMap[builtinExtensions[i].id] = builtinExtensions[i];
|
||||
}
|
||||
// Overwrite with extensions found in extra
|
||||
for (let i = 0, len = extraBuiltinExtensions.length; i < len; i++) {
|
||||
resultMap[extraBuiltinExtensions[i].id] = extraBuiltinExtensions[i];
|
||||
}
|
||||
|
||||
let resultArr = Object.keys(resultMap).map((id) => resultMap[id]);
|
||||
resultArr.sort((a, b) => {
|
||||
const aLastSegment = path.basename(a.extensionLocation.fsPath);
|
||||
const bLastSegment = path.basename(b.extensionLocation.fsPath);
|
||||
if (aLastSegment < bLastSegment) {
|
||||
return -1;
|
||||
}
|
||||
if (aLastSegment > bLastSegment) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
return resultArr;
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user