зеркало из https://github.com/microsoft/tfs-cli.git
Finish up localization support.
This commit is contained in:
Родитель
2c029d29dc
Коммит
2fd91dc70f
|
@ -31,3 +31,6 @@ node_modules
|
|||
|
||||
# VS Code Settings
|
||||
.settings
|
||||
|
||||
# Generated by nexe
|
||||
tmp/
|
|
@ -4,4 +4,5 @@ docs/
|
|||
tests/
|
||||
_build/tests
|
||||
gulpfile.js
|
||||
tsconfig.json
|
||||
tsconfig.json
|
||||
tmp/
|
|
@ -1,41 +1,87 @@
|
|||
{
|
||||
"version": "0.1.0",
|
||||
// List of configurations. Add new configurations or edit existing ones.
|
||||
"configurations": [
|
||||
{
|
||||
// Name of configuration; appears in the launch configuration drop down menu.
|
||||
"name": "Launch ./_build/app/tfx-cli.js",
|
||||
// Type of configuration.
|
||||
"type": "node",
|
||||
// Workspace relative or absolute path to the program.
|
||||
"program": "${workspaceRoot}/_build/app/tfx-cli.js",
|
||||
// Automatically stop program after launch.
|
||||
"stopOnEntry": true,
|
||||
// Command line arguments passed to the program.
|
||||
"args": ["workitem", "update", "--work-item-id", "11", "--values", "{\"title\":\"hello\"}"],
|
||||
// Workspace relative or absolute path to the working directory of the program being debugged. Default is the current workspace.
|
||||
"cwd": "${workspaceRoot}",
|
||||
// Workspace relative or absolute path to the runtime executable to be used. Default is the runtime executable on the PATH.
|
||||
"runtimeExecutable": null,
|
||||
// Optional arguments passed to the runtime executable.
|
||||
"runtimeArgs": ["--nolazy"],
|
||||
// Environment variables passed to the program.
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
},
|
||||
// Use JavaScript source maps (if they exist).
|
||||
"sourceMaps": true,
|
||||
// If JavaScript source maps are enabled, the generated code is expected in this directory.
|
||||
"outDir": "${workspaceRoot}/_build/app"
|
||||
},
|
||||
{
|
||||
"name": "Attach",
|
||||
"type": "node",
|
||||
// TCP/IP address. Default is "localhost".
|
||||
"address": "localhost",
|
||||
// Port to attach to.
|
||||
"port": 5858,
|
||||
"sourceMaps": false
|
||||
}
|
||||
]
|
||||
}
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Extension Create",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/app/app.ts",
|
||||
"stopOnEntry": false,
|
||||
"args": [
|
||||
"extension",
|
||||
"create",
|
||||
"--loc-root",
|
||||
"C:\\vso\\obj\\Debug.AnyCPU\\Vssf.WebPlatform\\MS.VS.Extension.WebPlatform\\resjson",
|
||||
"--manifests",
|
||||
"vss-web.json",
|
||||
"--override",
|
||||
"{\"version\": \"0.0.1\"}"
|
||||
],
|
||||
"cwd": "C:\\vso\\src\\Vssf\\WebPlatform\\ExtensionPackages\\WebPlatform",
|
||||
"preLaunchTask": null,
|
||||
"runtimeExecutable": null,
|
||||
"runtimeArgs": [
|
||||
"--nolazy"
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development",
|
||||
"TFX_TRACE": "1"
|
||||
},
|
||||
"console": "internalConsole",
|
||||
"sourceMaps": true,
|
||||
"outDir": "${workspaceRoot}/_build"
|
||||
},
|
||||
{
|
||||
"name": "Resources Create",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/app/app.ts",
|
||||
"stopOnEntry": false,
|
||||
"args": [
|
||||
"extension",
|
||||
"resources",
|
||||
"create",
|
||||
"--manifests",
|
||||
"vss-web.json",
|
||||
"--override",
|
||||
"{\"version\": \"0.0.1\"}"
|
||||
],
|
||||
"cwd": "C:\\vso\\src\\Vssf\\WebPlatform\\ExtensionPackages\\WebPlatform",
|
||||
"preLaunchTask": null,
|
||||
"runtimeExecutable": null,
|
||||
"runtimeArgs": [
|
||||
"--nolazy"
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development",
|
||||
"TFX_TRACE": "1"
|
||||
},
|
||||
"console": "internalConsole",
|
||||
"sourceMaps": true,
|
||||
"outDir": "${workspaceRoot}/_build"
|
||||
},
|
||||
{
|
||||
"name": "HelpScreen",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/app/app.ts",
|
||||
"stopOnEntry": false,
|
||||
"args": [
|
||||
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": null,
|
||||
"runtimeExecutable": null,
|
||||
"runtimeArgs": [
|
||||
"--nolazy"
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development",
|
||||
"TFX_TRACE": "1"
|
||||
},
|
||||
"console": "internalConsole",
|
||||
"sourceMaps": true,
|
||||
"outDir": "${workspaceRoot}/_build"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.svn": true,
|
||||
"**/.hg": true,
|
||||
"**/.DS_Store": true,
|
||||
"tmp/": true
|
||||
},
|
||||
"editor.insertSpaces": false
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "0.1.0",
|
||||
"command": "npm",
|
||||
"isShellCommand": true,
|
||||
"showOutput": "always",
|
||||
"suppressTaskName": true,
|
||||
"tasks": [
|
||||
{
|
||||
"taskName": "build",
|
||||
"args": ["run", "build"]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -4,7 +4,6 @@ import errHandler = require("./lib/errorhandler");
|
|||
import loader = require("./lib/loader");
|
||||
import path = require("path");
|
||||
|
||||
|
||||
// Set app root
|
||||
common.APP_ROOT = __dirname;
|
||||
|
||||
|
|
|
@ -63,10 +63,10 @@ export abstract class ExtensionComposer {
|
|||
return "All 'files' must include a 'path'.";
|
||||
}
|
||||
if (asset.Type && asset.Addressable) {
|
||||
if (usedAssetTypes[asset.Type]) {
|
||||
if (usedAssetTypes[asset.Type + "|" + asset.lang]) {
|
||||
return "Cannot have multiple 'addressable' files with the same 'assetType'.\nFile1: " + usedAssetTypes[asset.Type] + ", File 2: " + asset.Path + " (asset type: " + asset.Type + ")";
|
||||
} else {
|
||||
usedAssetTypes[asset.Type] = asset.Path;
|
||||
usedAssetTypes[asset.Type + "|" + asset.Lang] = asset.Path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ export interface PackageFiles {
|
|||
}
|
||||
|
||||
/**
|
||||
* Describes a file in a manifest
|
||||
*/
|
||||
* Describes a file in a manifest
|
||||
*/
|
||||
export interface FileDeclaration {
|
||||
/**
|
||||
* The type of this asset (Type attribute in the vsixmanifest's <Asset> entry)
|
||||
|
@ -154,6 +154,11 @@ export interface MergeSettings {
|
|||
* True to rev the version of the extension before packaging.
|
||||
*/
|
||||
revVersion: boolean;
|
||||
|
||||
/**
|
||||
* Path to the root of localized resource files
|
||||
*/
|
||||
locRoot: string;
|
||||
}
|
||||
|
||||
export interface PackageSettings {
|
||||
|
@ -166,7 +171,6 @@ export interface PackageSettings {
|
|||
* Path to the root of localized resource files
|
||||
*/
|
||||
locRoot: string;
|
||||
|
||||
}
|
||||
|
||||
export interface PublishSettings {
|
||||
|
@ -222,6 +226,11 @@ export interface ResourceSet {
|
|||
combined: ResourcesFile;
|
||||
}
|
||||
|
||||
export interface LocalizedResources {
|
||||
[languageTag: string]: ResourcesFile;
|
||||
defaults: ResourcesFile;
|
||||
}
|
||||
|
||||
/*** Types for VSIX Manifest ****/
|
||||
|
||||
export namespace Vsix {
|
||||
|
|
|
@ -109,11 +109,9 @@ export module LocPrep {
|
|||
public generateLocalizationKeys(): ResourceSet {
|
||||
this.initStringObjs();
|
||||
this.manifestBuilders.forEach((builder) => {
|
||||
if (builder.getType() !== VsixManifestBuilder.manifestType) {
|
||||
this.jsonReplaceWithKeysAndGenerateDefaultStrings(builder);
|
||||
}
|
||||
this.jsonReplaceWithKeysAndGenerateDefaultStrings(builder);
|
||||
});
|
||||
this.vsixGenerateDefaultStrings();
|
||||
|
||||
return {
|
||||
manifestResources: this.resourceFileMap,
|
||||
combined: this.generateCombinedResourceFile()
|
||||
|
@ -148,27 +146,6 @@ export module LocPrep {
|
|||
return str;
|
||||
}
|
||||
|
||||
private vsixGenerateDefaultStrings(): void {
|
||||
let vsixManifest = this.vsixManifestBuilder.getData();
|
||||
let displayName = this.removeI18nPrefix(_.get<string>(vsixManifest, "PackageManifest.Metadata[0].DisplayName[0]"));
|
||||
let description = this.removeI18nPrefix(_.get<string>(vsixManifest, "PackageManifest.Metadata[0].Description[0]._"));
|
||||
let releaseNotes = this.removeI18nPrefix(_.get<string>(vsixManifest, "PackageManifest.Metadata[0].ReleaseNotes[0]"));
|
||||
let vsixRes: ResourcesFile = {};
|
||||
if (displayName) {
|
||||
vsixRes["displayName"] = displayName;
|
||||
_.set<any, string>(vsixManifest, "PackageManifest.Metadata[0].DisplayName[0]", displayName);
|
||||
}
|
||||
if (displayName) {
|
||||
vsixRes["description"] = description;
|
||||
_.set<any, string>(vsixManifest, "PackageManifest.Metadata[0].Description[0]._", description);
|
||||
}
|
||||
if (releaseNotes) {
|
||||
vsixRes["releaseNotes"] = releaseNotes;
|
||||
_.set<any, string>(vsixManifest, "PackageManifest.Metadata[0].ReleaseNotes[0]", releaseNotes);
|
||||
}
|
||||
this.resourceFileMap[this.vsixManifestBuilder.getType()] = vsixRes;
|
||||
}
|
||||
|
||||
private jsonReplaceWithKeysAndGenerateDefaultStrings(builder: ManifestBuilder, json: any = null, path: string = ""): void {
|
||||
if (!json) {
|
||||
json = builder.getData();
|
||||
|
@ -176,13 +153,10 @@ export module LocPrep {
|
|||
for (let key in json) {
|
||||
let val = json[key];
|
||||
if (_.isObject(val)) {
|
||||
let nextPath = builder.getLocKeyPath(path + key + ".");
|
||||
while (_.endsWith(nextPath, ".")) {
|
||||
nextPath = nextPath.substr(0, nextPath.length - 1);
|
||||
}
|
||||
let nextPath = path + key + ".";
|
||||
this.jsonReplaceWithKeysAndGenerateDefaultStrings(builder, val, nextPath);
|
||||
} else if (_.isString(val) && _.startsWith(val, LocKeyGenerator.I18N_PREFIX)) {
|
||||
this.addResource(builder.getType(), key, path + key, json)
|
||||
this.addResource(builder.getType(), key, builder.getLocKeyPath(path + key), json)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
|
||||
import { PackageFiles, FileDeclaration, ResourcesFile } from "./interfaces";
|
||||
import { PackageFiles, FileDeclaration, LocalizedResources, ResourcesFile } from "./interfaces";
|
||||
import { cleanAssetPath, forwardSlashesPath, removeMetaKeys, toZipItemName } from "./utils";
|
||||
import _ = require("lodash");
|
||||
import common = require("../../../lib/common");
|
||||
|
@ -12,8 +12,9 @@ import trace = require('../../../lib/trace');
|
|||
|
||||
export abstract class ManifestBuilder {
|
||||
protected packageFiles: PackageFiles = { };
|
||||
protected lcPartNames: {[filename: string]: string} = { };
|
||||
protected lcPartNames: {[filename: string]: string} = { };
|
||||
protected data: any = { };
|
||||
private static resourcePrefix = "resource:";
|
||||
|
||||
constructor(private extRoot: string) { }
|
||||
|
||||
|
@ -47,7 +48,7 @@ export abstract class ManifestBuilder {
|
|||
/**
|
||||
* Called just before the package is written to make any final adjustments.
|
||||
*/
|
||||
public finalize(files: PackageFiles, builders: ManifestBuilder[]): Promise<void> {
|
||||
public finalize(files: PackageFiles, resourceData: LocalizedResources, builders: ManifestBuilder[]): Promise<void> {
|
||||
return Promise.resolve<void>(null);
|
||||
}
|
||||
|
||||
|
@ -71,35 +72,42 @@ export abstract class ManifestBuilder {
|
|||
* Gets the contents of the file that will serve as localization for this asset.
|
||||
* Default implementation returns JSON with all strings replaced given by the translations/defaults objects.
|
||||
*/
|
||||
public getLocResult(translations: ResourcesFile, defaults: ResourcesFile): string {
|
||||
return JSON.stringify(this._getLocResult(this.expandResourceFile(translations), this.expandResourceFile(defaults)), null, 4);
|
||||
public getLocResult(translations: ResourcesFile, defaults: ResourcesFile): FileDeclaration[] {
|
||||
return [{
|
||||
partName: this.getPath(),
|
||||
path: null,
|
||||
content: JSON.stringify(this._getLocResult(this.expandResourceFile(translations), this.expandResourceFile(defaults)), null, 4)
|
||||
}];
|
||||
}
|
||||
|
||||
private _getLocResult(translations: any, defaults: any, locData = {}, currentPath = "") {
|
||||
let currentData = currentPath ? _.get(this.data, currentPath) : this.data;
|
||||
private _getLocResult(translations: any, defaults: any, locData = {}, currentPath: string[] = []) {
|
||||
|
||||
// CurrentData should be guaranteed to be
|
||||
// deep iterate through this.data. If the value is a string that starts with
|
||||
// resource:, use the key to look in translations and defaults to find the real string.
|
||||
// Do the replacement
|
||||
|
||||
// This magically works for arrays too, just go with it.
|
||||
Object.keys(currentData).forEach((key) => {
|
||||
let nextPath = currentPath + "." + key;
|
||||
if (_.isString(currentData[key])) {
|
||||
let translation = _.get(translations, nextPath);
|
||||
if (translation !== undefined) {
|
||||
_.set(locData, nextPath, translation);
|
||||
let currentData = currentPath.length > 0 ? _.get(this.data, currentPath) : this.data;
|
||||
Object.keys(currentData).forEach(key => {
|
||||
const val = currentData[key];
|
||||
if (typeof val === "string" && val.substr(0, ManifestBuilder.resourcePrefix.length) === ManifestBuilder.resourcePrefix) {
|
||||
const locKey = val.substr(ManifestBuilder.resourcePrefix.length);
|
||||
const localized = _.get(translations, locKey) || _.get(defaults, locKey);
|
||||
if (localized) {
|
||||
_.set(locData, currentPath.concat(key), localized);
|
||||
} else {
|
||||
let defaultString = _.get(defaults, nextPath);
|
||||
if (defaultString !== undefined) {
|
||||
_.set(locData, nextPath, defaultString);
|
||||
} else {
|
||||
throw "Couldn't find a default string - this is definitely a bug.";
|
||||
}
|
||||
throw new Error("Could not find translation or default value for resource " + locKey);
|
||||
}
|
||||
} else if (_.isObject(currentData[key])) {
|
||||
this._getLocResult(translations, defaults, locData, nextPath);
|
||||
} else {
|
||||
// must be a number of boolean
|
||||
_.set(locData, nextPath, currentData[key]);
|
||||
if (typeof val === "object") {
|
||||
if (_.isArray(val)) {
|
||||
_.set(locData, currentPath.concat(key), []);
|
||||
} else {
|
||||
_.set(locData, currentPath.concat(key), {});
|
||||
}
|
||||
this._getLocResult(translations, defaults, locData, currentPath.concat(key));
|
||||
} else {
|
||||
_.set(locData, currentPath.concat(key), val);
|
||||
}
|
||||
}
|
||||
});
|
||||
return locData;
|
||||
|
@ -177,7 +185,7 @@ export abstract class ManifestBuilder {
|
|||
file.partName = "/" + path.relative(this.extRoot, file.path);
|
||||
}
|
||||
if (!file.partName) {
|
||||
throw "Every file must have a path specified name.";
|
||||
throw new Error("Every file must have a path specified name.");
|
||||
}
|
||||
|
||||
file.partName = forwardSlashesPath(file.partName);
|
||||
|
@ -208,11 +216,13 @@ export abstract class ManifestBuilder {
|
|||
this.packageFiles[file.path || common.newGuid()] = file;
|
||||
this.lcPartNames[file.partName.toLowerCase()] = file.partName;
|
||||
} else {
|
||||
throw "All files in the package must have a case-insensitive unique filename. Trying to add " + file.partName + ", but " + existPartName + " was already added to the package.";
|
||||
throw new Error("All files in the package must have a case-insensitive unique filename. Trying to add " + file.partName + ", but " + existPartName + " was already added to the package.");
|
||||
}
|
||||
}
|
||||
if (file.contentType && this.packageFiles[file.path]) {
|
||||
this.packageFiles[file.path].contentType = file.contentType;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
|
||||
|
||||
import { ManifestBuilder } from "./manifest";
|
||||
import { ComposerFactory } from "./extension-composer-factory";
|
||||
import { ExtensionComposer } from "./extension-composer";
|
||||
import { FileDeclaration, MergeSettings, PackageFiles, ResourceSet, TargetDeclaration } from "./interfaces";
|
||||
import { FileDeclaration, LocalizedResources, MergeSettings, PackageFiles, ResourceSet, ResourcesFile, TargetDeclaration } from "./interfaces";
|
||||
import _ = require("lodash");
|
||||
import fs = require("fs");
|
||||
import glob = require("glob");
|
||||
|
@ -192,29 +190,108 @@ export class Merger {
|
|||
let locPrepper = new loc.LocPrep.LocKeyGenerator(this.manifestBuilders);
|
||||
let resources = locPrepper.generateLocalizationKeys();
|
||||
|
||||
// Build up a master file list
|
||||
let packageFiles: PackageFiles = {};
|
||||
this.manifestBuilders.forEach((builder) => {
|
||||
_.assign(packageFiles, builder.files);
|
||||
});
|
||||
// Build up resource data by reading the translations from disk
|
||||
return this.buildResourcesData().then(resourceData => {
|
||||
if (resourceData) {
|
||||
resourceData["defaults"] = resources.combined;
|
||||
}
|
||||
|
||||
let components: VsixComponents = { builders: this.manifestBuilders, resources: resources };
|
||||
// Build up a master file list
|
||||
let packageFiles: PackageFiles = {};
|
||||
this.manifestBuilders.forEach((builder) => {
|
||||
_.assign(packageFiles, builder.files);
|
||||
});
|
||||
|
||||
// Finalize each builder
|
||||
return Promise.all([updateVersionPromise].concat(this.manifestBuilders.map(b => b.finalize(packageFiles, this.manifestBuilders)))).then(() => {
|
||||
// Let the composer do validation
|
||||
return this.extensionComposer.validate(components).then((validationResult) => {
|
||||
if (validationResult.length === 0 || this.settings.bypassValidation) {
|
||||
return components;
|
||||
} else {
|
||||
throw new Error("There were errors with your extension. Address the following and re-run the tool.\n" + validationResult);
|
||||
}
|
||||
let components: VsixComponents = { builders: this.manifestBuilders, resources: resources };
|
||||
|
||||
// Finalize each builder
|
||||
return Promise.all([updateVersionPromise].concat(this.manifestBuilders.map(b => b.finalize(packageFiles, resourceData, this.manifestBuilders)))).then(() => {
|
||||
// Let the composer do validation
|
||||
return this.extensionComposer.validate(components).then((validationResult) => {
|
||||
if (validationResult.length === 0 || this.settings.bypassValidation) {
|
||||
return components;
|
||||
} else {
|
||||
throw new Error("There were errors with your extension. Address the following and re-run the tool.\n" + validationResult);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* For each folder F under the localization folder (--loc-root),
|
||||
* look for a resources.resjson file within F. If it exists, split the
|
||||
* resources.resjson into one file per manifest. Add
|
||||
* each to the vsix archive as F/<manifest_loc_path> and F/Extension.vsixlangpack
|
||||
*/
|
||||
private buildResourcesData(): Promise<LocalizedResources> {
|
||||
|
||||
// Make sure locRoot is set, that it refers to a directory, and
|
||||
// iterate each subdirectory of that.
|
||||
if (!this.settings.locRoot) {
|
||||
return Promise.resolve<void[]>(null);
|
||||
}
|
||||
let stringsPath = path.resolve(this.settings.locRoot);
|
||||
|
||||
const data: LocalizedResources = {defaults: null};
|
||||
|
||||
// Check that --loc-root exists and is a directory.
|
||||
return Q.Promise((resolve, reject, notify) => {
|
||||
fs.exists(stringsPath, (exists) => {
|
||||
resolve(exists);
|
||||
});
|
||||
}).then<boolean>((exists) => {
|
||||
if (exists) {
|
||||
return Q.nfcall(fs.lstat, stringsPath).then((stats: fs.Stats) => {
|
||||
if (stats.isDirectory()) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return Q.resolve(false);
|
||||
}
|
||||
}).then<void[]>((stringsFolderExists) => {
|
||||
if (!stringsFolderExists) {
|
||||
return Promise.resolve<void[]>(null);
|
||||
}
|
||||
|
||||
// stringsPath exists and is a directory - read it.
|
||||
return <Promise<void[]>><any>Q.nfcall(fs.readdir, stringsPath).then((files: string[]) => {
|
||||
let promises: Promise<void>[] = [];
|
||||
files.forEach((languageTag) => {
|
||||
var filePath = path.join(stringsPath, languageTag);
|
||||
let promise = Q.nfcall(fs.lstat, filePath).then((fileStats: fs.Stats) => {
|
||||
if (fileStats.isDirectory()) {
|
||||
|
||||
// We're under a language tag directory within locRoot. Look for
|
||||
// resources.resjson and use that to generate manfiest files
|
||||
let resourcePath = path.join(filePath, "resources.resjson");
|
||||
return Q.Promise<boolean>((resolve, reject, notify) => {
|
||||
fs.exists(resourcePath, (exists) => {
|
||||
resolve(exists);
|
||||
});
|
||||
}).then<void>((exists: boolean) => {
|
||||
if (exists) {
|
||||
// A resources.resjson file exists in <locRoot>/<language_tag>/
|
||||
return Q.nfcall<string>(fs.readFile, resourcePath, "utf8").then<void>((contents: string) => {
|
||||
let resourcesObj = JSON.parse(contents);
|
||||
data[languageTag] = resourcesObj;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
promises.push(promise);
|
||||
});
|
||||
return Promise.all(promises);
|
||||
});
|
||||
}).then(() => {
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively converts a given path to a flat list of FileDeclaration
|
||||
* @TODO: Async.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ManifestBuilder } from "../../manifest"
|
||||
import { PackageFiles } from "../../interfaces"
|
||||
import { LocalizedResources, PackageFiles } from "../../interfaces"
|
||||
import _ = require("lodash");
|
||||
import os = require("os");
|
||||
|
||||
|
@ -26,7 +26,7 @@ export class VsoManifestBuilder extends ManifestBuilder {
|
|||
return "application/json";
|
||||
}
|
||||
|
||||
public finalize(files: PackageFiles): Promise<void> {
|
||||
public finalize(files: PackageFiles, resourceData: LocalizedResources): Promise<void> {
|
||||
// Ensure some default values are set
|
||||
if (!this.data.contributions) {
|
||||
this.data.contributions = [];
|
||||
|
@ -53,10 +53,14 @@ export class VsoManifestBuilder extends ManifestBuilder {
|
|||
if (pathParts && pathParts.length >= 2) {
|
||||
let cIndex = parseInt(pathParts[1]);
|
||||
if (pathParts[0] === "contributions" && !isNaN(cIndex) && this.data.contributions[cIndex] && this.data.contributions[cIndex].id) {
|
||||
return "contributions" + this.data.contributions[cIndex].id;
|
||||
return _.trimEnd("contributions." + this.data.contributions[cIndex].id + "." + pathParts.slice(2).join("."));
|
||||
} else if (pathParts[0] === "contributionTypes" && !isNaN(cIndex) && this.data.contributionTypes[cIndex] && this.data.contributionTypes[cIndex].id) {
|
||||
return _.trimEnd("contributionTypes." + this.data.contributionTypes[cIndex].id + "." + pathParts.slice(2).join("."));
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ManifestBuilder } from "./manifest";
|
||||
import { FileDeclaration, PackageFiles, ResourcesFile, ScreenshotDeclaration, TargetDeclaration, BadgeDeclaration, Vsix, VsixLanguagePack } from "./interfaces";
|
||||
import { FileDeclaration, LocalizedResources, PackageFiles, ResourcesFile, ScreenshotDeclaration, TargetDeclaration, BadgeDeclaration, Vsix, VsixLanguagePack } from "./interfaces";
|
||||
import { cleanAssetPath, jsonToXml, maxKey, removeMetaKeys, toZipItemName } from "./utils";
|
||||
import _ = require("lodash");
|
||||
import childProcess = require("child_process");
|
||||
|
@ -81,9 +81,13 @@ export class VsixManifestBuilder extends ManifestBuilder {
|
|||
/**
|
||||
* Gets the contents of the vsixLangPack file for this manifest
|
||||
*/
|
||||
public getLocResult(translations: ResourcesFile, defaults: ResourcesFile): string {
|
||||
public getLocResult(translations: ResourcesFile, defaults: ResourcesFile): FileDeclaration[] {
|
||||
let langPack = this.generateVsixLangPack(translations, defaults);
|
||||
return jsonToXml(langPack);
|
||||
return [{
|
||||
partName: "Extension.vsixlangpack",
|
||||
path: null,
|
||||
content: jsonToXml(langPack)
|
||||
}];
|
||||
}
|
||||
|
||||
private generateVsixLangPack(translations: ResourcesFile, defaults: ResourcesFile): VsixLanguagePack {
|
||||
|
@ -93,9 +97,9 @@ export class VsixManifestBuilder extends ManifestBuilder {
|
|||
Version: "1.0.0",
|
||||
xmlns: "http://schemas.microsoft.com/developer/vsx-schema-lp/2010"
|
||||
},
|
||||
LocalizedName: [translations["displayName"] || defaults["displayName"]],
|
||||
LocalizedDescription: [translations["description"] || defaults["description"]],
|
||||
LocalizedReleaseNotes: [translations["releaseNotes"] || defaults["releaseNotes"]],
|
||||
LocalizedName: [translations["displayName"] || defaults["displayName"] || null],
|
||||
LocalizedDescription: [translations["description"] || defaults["description"] || null],
|
||||
LocalizedReleaseNotes: [translations["releaseNotes"] || defaults["releaseNotes"] || null],
|
||||
License: [null],
|
||||
MoreInfoUrl: [null]
|
||||
}
|
||||
|
@ -371,6 +375,21 @@ export class VsixManifestBuilder extends ManifestBuilder {
|
|||
return _.get<string>(this.data, "PackageManifest.Metadata[0].Identity[0].$.Id");
|
||||
}
|
||||
|
||||
/**
|
||||
* The JSON structure is fairly exotic since the result is an XML file,
|
||||
* so change those exotic keys to easy-to-read ones.
|
||||
*/
|
||||
public getLocKeyPath(path: string): string {
|
||||
switch (path) {
|
||||
case "PackageManifest.Metadata.0.Description.0._":
|
||||
return "description";
|
||||
case "PackageManifest.Metadata.0.DisplayName.0" :
|
||||
return "displayName";
|
||||
default :
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the publisher this vsixmanifest goes to
|
||||
*/
|
||||
|
@ -382,7 +401,7 @@ export class VsixManifestBuilder extends ManifestBuilder {
|
|||
* --Ensures an <Asset> entry is added for each file as appropriate
|
||||
* --Builds the [Content_Types].xml file
|
||||
*/
|
||||
public finalize(files: PackageFiles, builders: ManifestBuilder[]): Promise<void> {
|
||||
public finalize(files: PackageFiles, resourceData: LocalizedResources, builders: ManifestBuilder[]): Promise<void> {
|
||||
// Default installation target to VSS if not provided (and log warning)
|
||||
let installationTarget = _.get<any[]>(this.data, "PackageManifest.Installation[0].InstallationTarget");
|
||||
if (!(_.isArray(installationTarget) && installationTarget.length > 0)) {
|
||||
|
@ -396,6 +415,32 @@ export class VsixManifestBuilder extends ManifestBuilder {
|
|||
]);
|
||||
}
|
||||
|
||||
if (resourceData) {
|
||||
Object.keys(resourceData).forEach(languageTag => {
|
||||
if (languageTag === "defaults") {
|
||||
return;
|
||||
}
|
||||
builders.forEach(builder => {
|
||||
const locResult = builder.getLocResult(resourceData[languageTag], resourceData.defaults);
|
||||
locResult.forEach(lr => {
|
||||
lr.lang = languageTag;
|
||||
lr.partName = `${languageTag}/${lr.partName}`;
|
||||
if (lr.partName.indexOf("vsixlangpack") === -1) {
|
||||
lr.assetType = builder.getType();
|
||||
lr.addressable = true;
|
||||
} else {
|
||||
lr.addressable = false;
|
||||
}
|
||||
|
||||
const file = this.addFile(lr);
|
||||
if (file.assetType) {
|
||||
this.addAssetToManifest(file.partName, file.assetType, file.addressable, file.lang);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Object.keys(files).forEach((fileName) => {
|
||||
let file = files[fileName];
|
||||
|
||||
|
@ -413,10 +458,11 @@ export class VsixManifestBuilder extends ManifestBuilder {
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
// The vsixmanifest will be responsible for generating the [Content_Types].xml file
|
||||
// Obviously this is kind of strange, but hey ho.
|
||||
return this.genContentTypesXml(builders).then((result) => {
|
||||
this.addFile({
|
||||
this.addAsset({
|
||||
path: null,
|
||||
content: result,
|
||||
partName: "/[Content_Types].xml"
|
||||
|
@ -424,6 +470,7 @@ export class VsixManifestBuilder extends ManifestBuilder {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the string representation (XML) of this manifest
|
||||
*/
|
||||
|
@ -456,7 +503,7 @@ export class VsixManifestBuilder extends ManifestBuilder {
|
|||
let contentTypePromises: Promise<any>[] = [];
|
||||
let extensionlessFiles = [];
|
||||
let uniqueExtensions = _.uniq<string>(Object.keys(this.files).map((f) => {
|
||||
let extName = path.extname(f);
|
||||
let extName = path.extname(f) || path.extname(this.files[f].partName);
|
||||
const filename = path.basename(f);
|
||||
|
||||
// Look in the best guess table. Or, default to text/plain if the file starts with a "."
|
||||
|
|
|
@ -53,17 +53,18 @@ export class VsixWriter {
|
|||
* Otherwise, try to determine if outPath is a directory (checking for a . in the filename)
|
||||
* If it is, generate an automatic filename in the given outpath
|
||||
* Otherwise, outPath doesn't change.
|
||||
* If filename is generated automatically, use fileExt as the extension
|
||||
*/
|
||||
private getOutputPath(outPath: string): string {
|
||||
public getOutputPath(outPath: string, fileExt: string = "vsix"): string {
|
||||
// Find the vsix manifest, if it exists
|
||||
let vsixBuilders = this.manifestBuilders.filter(b => b.getType() === VsixManifestBuilder.manifestType);
|
||||
let autoName = "extension.vsix";
|
||||
let autoName = "extension." + fileExt;
|
||||
if (vsixBuilders.length === 1) {
|
||||
let vsixManifest = vsixBuilders[0].getData();
|
||||
let pub = _.get(vsixManifest, "PackageManifest.Metadata[0].Identity[0].$.Publisher");
|
||||
let ns = _.get(vsixManifest, "PackageManifest.Metadata[0].Identity[0].$.Id");
|
||||
let version = _.get(vsixManifest, "PackageManifest.Metadata[0].Identity[0].$.Version");
|
||||
autoName = pub + "." + ns + "-" + version + ".vsix";
|
||||
autoName = `${pub}.${ns}-${version}.${fileExt}`;
|
||||
}
|
||||
|
||||
if (outPath === "{auto}") {
|
||||
|
@ -148,8 +149,6 @@ export class VsixWriter {
|
|||
builderPromises.push(builderPromise);
|
||||
});
|
||||
return Promise.all(builderPromises).then(() => {
|
||||
return this.addResourceStrings(vsix);
|
||||
}).then(() => {
|
||||
trace.debug("Writing vsix to: %s", outputPath);
|
||||
|
||||
return Q.nfcall(mkdirp, path.dirname(outputPath)).then(() => {
|
||||
|
@ -175,6 +174,8 @@ export class VsixWriter {
|
|||
return Promise.resolve<void[]>(null);
|
||||
}
|
||||
let stringsPath = path.resolve(this.settings.locRoot);
|
||||
|
||||
// Check that --loc-root exists and is a directory.
|
||||
return Q.Promise((resolve, reject, notify) => {
|
||||
fs.exists(stringsPath, (exists) => {
|
||||
resolve(exists);
|
||||
|
@ -189,16 +190,21 @@ export class VsixWriter {
|
|||
} else {
|
||||
return Q.resolve(false);
|
||||
}
|
||||
}).then<void[]>((stringsFolderExists) => {
|
||||
}).then<void[]>((stringsFolderExists) => {1
|
||||
if (!stringsFolderExists) {
|
||||
return Promise.resolve<void[]>(null);
|
||||
}
|
||||
|
||||
// stringsPath exists and is a directory - read it.
|
||||
return <Promise<void[]>><any>Q.nfcall(fs.readdir, stringsPath).then((files: string[]) => {
|
||||
let promises: Promise<void>[] = [];
|
||||
files.forEach((languageTag) => {
|
||||
var filePath = path.join(stringsPath, languageTag);
|
||||
let promise = Q.nfcall(fs.lstat, filePath).then((fileStats: fs.Stats) => {
|
||||
if (fileStats.isDirectory()) {
|
||||
|
||||
// We're under a language tag directory within locRoot. Look for
|
||||
// resources.resjson and use that to generate manfiest files
|
||||
let resourcePath = path.join(filePath, "resources.resjson");
|
||||
return Q.Promise<boolean>((resolve, reject, notify) => {
|
||||
fs.exists(resourcePath, (exists) => {
|
||||
|
@ -207,26 +213,36 @@ export class VsixWriter {
|
|||
}).then<void>((exists: boolean) => {
|
||||
if (exists) {
|
||||
// A resources.resjson file exists in <locRoot>/<language_tag>/
|
||||
// return Q.nfcall<string>(fs.readFile, resourcePath, "utf8").then<void>((contents: string) => {
|
||||
// let resourcesObj = JSON.parse(contents);
|
||||
// let locGen = new LocPrep.LocKeyGenerator(null, null);
|
||||
// let splitRes = locGen.splitIntoVsoAndVsixResourceObjs(resourcesObj);
|
||||
// let locManifestPath = languageTag + "/" + VsixWriter.VSO_MANIFEST_FILENAME;
|
||||
// vsix.file(toZipItemName(locManifestPath), this.getVsoManifestString(splitRes.vsoResources));
|
||||
// this.vsixManifest.PackageManifest.Assets[0].Asset.push({
|
||||
// "$": {
|
||||
// Lang: languageTag,
|
||||
// Type: "Microsoft.VisualStudio.Services.Manifest",
|
||||
// Path: locManifestPath,
|
||||
// Addressable: "true",
|
||||
// "d:Source": "File"
|
||||
// }
|
||||
// });
|
||||
return Q.nfcall<string>(fs.readFile, resourcePath, "utf8").then<void>((contents: string) => {
|
||||
let resourcesObj = JSON.parse(contents);
|
||||
|
||||
// let builder = new xml.Builder(VsixWriter.DEFAULT_XML_BUILDER_SETTINGS);
|
||||
// let vsixLangPackStr = builder.buildObject(splitRes.vsixResources);
|
||||
// vsix.file(toZipItemName(languageTag + "/Extension.vsixlangpack"), vsixLangPackStr);
|
||||
// });
|
||||
// For each language, go through each builder and generate its
|
||||
// localized resources.
|
||||
this.manifestBuilders.forEach(builder => {
|
||||
const locFiles = builder.getLocResult(resourcesObj, null);
|
||||
locFiles.forEach(locFile => {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
let locGen = new LocPrep.LocKeyGenerator(null);
|
||||
// let splitRes = locGen.splitIntoVsoAndVsixResourceObjs(resourcesObj);
|
||||
// let locManifestPath = languageTag + "/" + VsixWriter.VSO_MANIFEST_FILENAME;
|
||||
// vsix.file(toZipItemName(locManifestPath), this.getVsoManifestString(splitRes.vsoResources));
|
||||
// this.vsixManifest.PackageManifest.Assets[0].Asset.push({
|
||||
// "$": {
|
||||
// Lang: languageTag,
|
||||
// Type: "Microsoft.VisualStudio.Services.Manifest",
|
||||
// Path: locManifestPath,
|
||||
// Addressable: "true",
|
||||
// "d:Source": "File"
|
||||
// }
|
||||
// });
|
||||
|
||||
// let builder = new xml.Builder(VsixWriter.DEFAULT_XML_BUILDER_SETTINGS);
|
||||
// let vsixLangPackStr = builder.buildObject(splitRes.vsixResources);
|
||||
// vsix.file(toZipItemName(languageTag + "/Extension.vsixlangpack"), vsixLangPackStr);
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve<void>(null);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
import { Merger } from "./_lib/merger";
|
||||
import { VsixManifestBuilder } from "./_lib/vsix-manifest-builder";
|
||||
import { MergeSettings, PackageSettings } from "./_lib/interfaces";
|
||||
|
|
|
@ -68,6 +68,7 @@ export class ExtensionBase<T> extends TfCommand<ExtensionArguments, T> {
|
|||
protected getMergeSettings(): Promise<MergeSettings> {
|
||||
return Promise.all([
|
||||
this.commandArgs.root.val(),
|
||||
this.commandArgs.locRoot.val(),
|
||||
this.commandArgs.manifests.val(),
|
||||
this.commandArgs.manifestGlobs.val(),
|
||||
this.commandArgs.override.val(),
|
||||
|
@ -77,7 +78,7 @@ export class ExtensionBase<T> extends TfCommand<ExtensionArguments, T> {
|
|||
this.commandArgs.publisher.val(true),
|
||||
this.commandArgs.extensionId.val(true)
|
||||
]).then<MergeSettings>((values) => {
|
||||
const [root, manifests, manifestGlob, override, overridesFile, revVersion, bypassValidation, publisher, extensionId] = values;
|
||||
const [root, locRoot, manifests, manifestGlob, override, overridesFile, revVersion, bypassValidation, publisher, extensionId] = values;
|
||||
if (publisher) {
|
||||
_.set(override, "publisher", publisher);
|
||||
}
|
||||
|
@ -106,6 +107,7 @@ export class ExtensionBase<T> extends TfCommand<ExtensionArguments, T> {
|
|||
_.merge(mergedOverrides, contentJSON, override);
|
||||
return {
|
||||
root: root[0],
|
||||
locRoot: locRoot && locRoot[0],
|
||||
manifests: manifests,
|
||||
manifestGlobs: manifestGlob,
|
||||
overrides: mergedOverrides,
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import { Merger } from "../_lib/merger";
|
||||
import { TfCommand } from "../../../lib/tfcommand";
|
||||
import { VsixWriter } from "../_lib/vsix-writer";
|
||||
import * as Loc from "../_lib/loc";
|
||||
import colors = require("colors");
|
||||
import extBase = require("../default");
|
||||
import trace = require('../../../lib/trace');
|
||||
|
||||
export function getCommand(args: string[]): TfCommand<extBase.ExtensionArguments, GenResourcesResult> {
|
||||
return new GenerateExtensionResources(args);
|
||||
}
|
||||
|
||||
export interface GenResourcesResult {
|
||||
resjsonPath: string;
|
||||
}
|
||||
|
||||
export class GenerateExtensionResources extends extBase.ExtensionBase<GenResourcesResult> {
|
||||
protected description = "Create a vsix package for an extension.";
|
||||
|
||||
constructor(passedArgs: string[]) {
|
||||
super(passedArgs, false);
|
||||
}
|
||||
|
||||
protected getHelpArgs(): string[] {
|
||||
return ["root", "manifests", "manifestGlobs", "override", "overridesFile", "revVersion", "bypassValidation", "publisher", "extensionId", "outputPath", "locRoot"];
|
||||
}
|
||||
|
||||
public exec(): Promise<GenResourcesResult> {
|
||||
return this.getMergeSettings().then(mergeSettings => {
|
||||
return this.getPackageSettings().then(packageSettings => {
|
||||
return new Merger(mergeSettings).merge().then(components => {
|
||||
const writer = new VsixWriter(packageSettings, components);
|
||||
const resjsonPath = writer.getOutputPath(packageSettings.outputPath, "resjson");
|
||||
Loc.LocPrep.writeResourceFile(resjsonPath, components.resources.combined);
|
||||
return <GenResourcesResult>{
|
||||
resjsonPath: writer.getOutputPath(packageSettings.outputPath, "resjson")
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected friendlyOutput(data: GenResourcesResult): void {
|
||||
trace.info(colors.green("\n=== Completed operation: generate extension resources ==="));
|
||||
trace.info(" - .resjson: %s", data.resjsonPath);
|
||||
}
|
||||
}
|
|
@ -21,12 +21,12 @@ export function getCommand(): Promise<TFXCommand> {
|
|||
let currentHierarchy = hierarchy;
|
||||
let inArgs = false;
|
||||
args.forEach((arg) => {
|
||||
if (currentHierarchy && currentHierarchy[arg] !== undefined) {
|
||||
currentHierarchy = currentHierarchy[arg];
|
||||
execPath.push(arg);
|
||||
} else if (arg.substr(0, 2) === "--" || inArgs) {
|
||||
if (arg.substr(0, 2) === "--" || inArgs) {
|
||||
commandArgs.push(arg);
|
||||
inArgs = true;
|
||||
} else if ((currentHierarchy && currentHierarchy[arg] !== undefined)) {
|
||||
currentHierarchy = currentHierarchy[arg];
|
||||
execPath.push(arg);
|
||||
} else {
|
||||
throw "Command '" + arg + "' not found. For help, type tfx " + execPath.join(" ") + " --help";
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ function getCommandHierarchy(root: string): Promise<CommandHierarchy> {
|
|||
return fs.readdir(root).then((files) => {
|
||||
let filePromises = [];
|
||||
files.forEach((file) => {
|
||||
if (file.substr(0, 1) === "_") {
|
||||
if (file.startsWith("_") || file.endsWith(".map")) {
|
||||
return;
|
||||
}
|
||||
let fullPath = path.resolve(root, file);
|
||||
|
|
|
@ -51,4 +51,4 @@ export function load(execPath: string[], args): Promise<TfCommand<any, any>> {
|
|||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -5,10 +5,10 @@ import Q = require("q");
|
|||
|
||||
// This is an fs lib that uses Q instead of callbacks.
|
||||
|
||||
export var W_OK = fs.W_OK;
|
||||
export var R_OK = fs.R_OK;
|
||||
export var X_OK = fs.X_OK;
|
||||
export var F_OK = fs.F_OK;
|
||||
export var W_OK = fs.constants ? fs.constants.W_OK : (fs as any).W_OK; // back-compat
|
||||
export var R_OK = fs.constants ? fs.constants.R_OK : (fs as any).R_OK; // back-compat
|
||||
export var X_OK = fs.constants ? fs.constants.X_OK : (fs as any).X_OK; // back-compat
|
||||
export var F_OK = fs.constants ? fs.constants.F_OK : (fs as any).F_OK; // back-compat
|
||||
|
||||
export function readdir(path: string): Promise<string[]> {
|
||||
return Q.nfcall<string[]>(fs.readdir, path);
|
||||
|
@ -63,7 +63,7 @@ export function fileAccess(path: string, mode: number = F_OK): Promise<boolean>
|
|||
export function canWriteTo(path: string): Promise<boolean> {
|
||||
return exists(path).then((exists) => {
|
||||
if (exists) {
|
||||
return fileAccess(path, fs.W_OK);
|
||||
return fileAccess(path, W_OK);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -12,29 +12,12 @@ Install Dev Dependencies from root of repo:
|
|||
|
||||
## Build
|
||||
|
||||
We build the product using gulp. Just type `gulp` in the root of the repo.
|
||||
We build the product using npm. Just type `npm run build` in the root of the repo.
|
||||
|
||||
This builds the product in the _build/app directory
|
||||
|
||||
```bash
|
||||
~/Projects/tfs-cli$ gulp
|
||||
[16:26:47] Using gulpfile ~/Projects/tfs-cli/gulpfile.js
|
||||
[16:26:47] Starting 'clean'...
|
||||
[16:26:47] Finished 'clean' after 16 ms
|
||||
[16:26:47] Starting 'compileApp'...
|
||||
[16:26:47] Starting 'compileTests'...
|
||||
[16:26:47] Starting 'resources'...
|
||||
[16:26:47] Finished 'resources' after 39 ms
|
||||
[16:26:47] Starting 'copy'...
|
||||
[16:26:47] Finished 'copy' after 15 ms
|
||||
[16:26:47] Compiling TypeScript files using tsc version 1.5.3
|
||||
[16:26:47] Compiling TypeScript files using tsc version 1.5.3
|
||||
[16:26:49] Finished 'compileTests' after 2.06 s
|
||||
[16:26:49] Finished 'compileApp' after 2.87 s
|
||||
[16:26:49] Starting 'build'...
|
||||
[16:26:49] Finished 'build' after 16 μs
|
||||
[16:26:49] Starting 'default'...
|
||||
[16:26:49] Finished 'default' after 3.56 μs
|
||||
C:\tfs-cli>npm run build
|
||||
```
|
||||
|
||||
## Install for Verification
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
echo Gulp is no longer used to build this repo. Please type 'npm run build' to build TFX. See the README for more details.
|
59
gulpfile.js
59
gulpfile.js
|
@ -1,59 +0,0 @@
|
|||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var gulp = require('gulp');
|
||||
var del = require('del');
|
||||
var mocha = require('gulp-mocha');
|
||||
var tsb = require('gulp-tsb');
|
||||
var filter = require('gulp-filter');
|
||||
var minimist = require('minimist');
|
||||
var shell = require('shelljs');
|
||||
var tsconfig = require('./tsconfig.json');
|
||||
var compilation = tsb.create(tsconfig.compilerOptions);
|
||||
|
||||
var sources = [
|
||||
'app/**',
|
||||
'tests/**',
|
||||
'typings/**/*.d.ts'
|
||||
];
|
||||
|
||||
gulp.task('copy', ['clean'], function() {
|
||||
return gulp.src(['package.json', './README.md', './app/tfs-cli.js'])
|
||||
.pipe(gulp.dest(path.join('_build', 'app')));
|
||||
});
|
||||
|
||||
gulp.task('compile', ['clean', 'copy'], function () {
|
||||
var tsFilter = filter('**/*.ts', { restore: true });
|
||||
|
||||
return gulp.src(sources, { base: '.' })
|
||||
.pipe(tsFilter)
|
||||
.pipe(compilation())
|
||||
.pipe(tsFilter.restore)
|
||||
.pipe(gulp.dest('_build'));
|
||||
});
|
||||
|
||||
gulp.task('build', ['clean', 'compile', 'copy']);
|
||||
|
||||
var mopts = {
|
||||
boolean: 'ci',
|
||||
string: 'suite',
|
||||
default: { ci: false, suite: '*' }
|
||||
};
|
||||
|
||||
var options = minimist(process.argv.slice(2), mopts);
|
||||
|
||||
gulp.task('test', function () {
|
||||
var suitePath = path.join('_build', 'tests', '*.js');
|
||||
console.log(suitePath);
|
||||
if (options.suite !== '*') {
|
||||
suitePath = path.join('_build', 'tests', options.suite + '.js');
|
||||
}
|
||||
|
||||
return gulp.src([suitePath])
|
||||
.pipe(mocha({ reporter: 'spec', ui: 'bdd', useColors: !options.ci }));
|
||||
});
|
||||
|
||||
gulp.task('clean', function (done) {
|
||||
del(['_build'], done);
|
||||
});
|
||||
|
||||
gulp.task('default', ['build']);
|
15
package.json
15
package.json
|
@ -14,7 +14,10 @@
|
|||
"scripts": {
|
||||
"clean": "rimraf _build",
|
||||
"prebuild": "npm run clean",
|
||||
"build": "tsc -p . && ncp app/tfx-cli.js _build/tfx-cli.js && ncp package.json _build/package.json"
|
||||
"build": "tsc -p .",
|
||||
"postbuild": "npm run copy",
|
||||
"copy": "ncp app/tfx-cli.js _build/tfx-cli.js && ncp package.json _build/package.json",
|
||||
"prepublish": "npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/colors": "^0.6.31",
|
||||
|
@ -33,7 +36,7 @@
|
|||
"archiver": "0.14.4",
|
||||
"async": "^1.4.0",
|
||||
"colors": "^1.1.2",
|
||||
"copy-paste": "^1.1.3",
|
||||
"copy-paste": "^1.3.0",
|
||||
"glob": "5.0.10",
|
||||
"inquirer": "0.8.5",
|
||||
"json-in-place": "^1.0.1",
|
||||
|
@ -57,16 +60,12 @@
|
|||
"xml2js": "^0.4.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"del": "^1.2.0",
|
||||
"gulp": "^3.9.0",
|
||||
"gulp-filter": "^3.0.1",
|
||||
"gulp-mocha": "2.0.0",
|
||||
"gulp-tsb": "^1.10.2",
|
||||
"minimatch": "^2.0.8",
|
||||
"mocha": "^2.2.5",
|
||||
"ncp": "^2.0.0",
|
||||
"rimraf": "^2.5.4",
|
||||
"typescript": "^2.0.2"
|
||||
"typescript": "^2.0.2",
|
||||
"webpack": "^1.13.2"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"target": "es5",
|
||||
"moduleResolution": "node",
|
||||
"declaration": false,
|
||||
"sourceMap": false,
|
||||
"sourceMap": true,
|
||||
"newLine": "LF",
|
||||
"lib": ["es5", "es2015", "es6"],
|
||||
"outDir": "_build"
|
||||
|
|
Загрузка…
Ссылка в новой задаче