diff --git a/src/gulpmain.ts b/src/gulpmain.ts index 7938f200..6eec35a6 100644 --- a/src/gulpmain.ts +++ b/src/gulpmain.ts @@ -36,7 +36,7 @@ gulp.task("rebuild", ["clean"], function (callback: Function): void { gulp.task("run-stylecop", function (callback: Function): void { if (fs.existsSync(buildConfig.copPath)) { var styleCop = new stylecopUtil.StyleCopUtil(); - styleCop.runCop(path.join(buildConfig.src, ".."), buildConfig.copPath, callback); + styleCop.runCop(buildConfig.src, buildConfig.copPath, callback); } else { callback(); } @@ -51,6 +51,7 @@ gulp.task("clean", function (callback: Function): void { gulp.task("copy", function (callback: Function): void { gulp.src(path.join(buildConfig.src, "/**/package.json")).pipe(gulp.dest(buildConfig.bin)); gulp.src(path.join(buildConfig.src, "/**/resources.json")).pipe(gulp.dest(buildConfig.bin)); + gulp.src(path.join(buildConfig.src, "/**/test/**")).pipe(gulp.dest(buildConfig.bin)); }); /* auto-generate taco-utils.d.ts*/ diff --git a/src/taco-cli/compile/typescript-util.ts b/src/taco-cli/compile/typescript-util.ts index 76369a50..49fa0b15 100644 --- a/src/taco-cli/compile/typescript-util.ts +++ b/src/taco-cli/compile/typescript-util.ts @@ -44,7 +44,6 @@ export class TypeScriptServices { if (!fs.statSync(currentPath).isDirectory()) { /* push the typescript files */ if (path.extname(currentPath) === ".ts" && - !currentPath.match("d.ts$") && !currentPath.match("gulpfile.ts") && !currentPath.match("gulpmain.ts")) { result.push(currentPath); @@ -79,8 +78,12 @@ export class TypeScriptServices { */ private logDiagnosticMessage(diagnostic: ts.Diagnostic): void { var sourceFile = diagnostic.file; - var lineAndCharacter = sourceFile.getLineAndCharacterFromPosition(diagnostic.start); - console.warn(sourceFile.filename + "(" + lineAndCharacter.line + "," + lineAndCharacter.character + "): " + diagnostic.messageText); + if (sourceFile) { + var lineAndCharacter = sourceFile.getLineAndCharacterFromPosition(diagnostic.start); + console.warn(sourceFile.filename + "(" + lineAndCharacter.line + "," + lineAndCharacter.character + "): " + diagnostic.messageText); + } else { + console.warn(diagnostic.messageText); + } } } diff --git a/src/taco-utils/package.json b/src/taco-utils/package.json index eb5f6f4f..fd20eb14 100644 --- a/src/taco-utils/package.json +++ b/src/taco-utils/package.json @@ -7,9 +7,13 @@ "version": "0.0.1", "main": "taco-utils.js", "dependencies": { + "elementtree": "0.1.6", + "unorm": "1.3.3" }, "private": true, "devDependencies": { - "typescript": ">=1.4.1" + "typescript": ">=1.4.1", + "mocha": "2.0.1", + "should": "4.3.0" } -} \ No newline at end of file +} diff --git a/src/taco-utils/taco-utils.ts b/src/taco-utils/taco-utils.ts index 58f92bac..25da100e 100644 --- a/src/taco-utils/taco-utils.ts +++ b/src/taco-utils/taco-utils.ts @@ -1,39 +1,71 @@ /// +/// +/// "use strict"; +import et = require ("elementtree"); import fs = require ("fs"); import path = require ("path"); +var unorm = require("unorm"); // Note no import: the compiler will remove the require since we don't use the unorm object, we just need it to add String.normalize module TacoUtility { // put more classes here, known limitation that classes in external modules CANNOT span multiple files export class ResourcesManager { - private static Resources: any = null; - private static DefaultLanguage: string = "en"; + private static Resources: { [key: string]: any; } = null; + private static SupportedLanguages: string[] = null; + private static DefaultLanguage: string = "en"; - public static init(language: string, resourcesDir?: string): void { + public static init(language: string, resourcesDir?: string): void { if (!resourcesDir) { resourcesDir = path.join(__dirname, "..", "resources"); } - var lang = this.bestLanguageMatchOrDefault(language, resourcesDir); - this.Resources = this.loadLanguage(lang, resourcesDir); + ResourcesManager.Resources = {}; + ResourcesManager.SupportedLanguages = []; + fs.readdirSync(resourcesDir).forEach(function (filename: string): void { + try { + ResourcesManager.Resources[filename.toLowerCase()] = ResourcesManager.loadLanguage(filename, resourcesDir); + ResourcesManager.SupportedLanguages.push(filename.toLowerCase()); + } catch (e) { + // Folder was not a valid resource; ignore it + } + }); + + ResourcesManager.DefaultLanguage = ResourcesManager.bestLanguageMatchOrDefault(language); } - /* ...optionalArgs is only there for typings, function rest params */ + // For unit tests + public static teardown(): void { + ResourcesManager.Resources = null; + ResourcesManager.SupportedLanguages = null; + } + + /** ...optionalArgs is only there for typings, function rest params */ public static getString(id: string, ...optionalArgs: any[]): string { - if (!this.Resources) { + var args = ResourcesManager.getOptionalArgsArrayFromFunctionCall(arguments, 1); + return ResourcesManager.getStringForLanguage(ResourcesManager.DefaultLanguage, id, args); + } + + /** ** ...optionalArgs is only there for typings, function rest params** */ + public static getStringForLanguage(requestOrAcceptLangs: any, id: string, ...optionalArgs: any[]): string { + if (!ResourcesManager.Resources) { throw new Error("Resources have not been loaded"); } - var s = this.Resources[id]; + var lang = ResourcesManager.bestLanguageMatchOrDefault(requestOrAcceptLangs); + + var s = ResourcesManager.Resources[lang][id]; if (!s) { return s; + } else if (Array.isArray(s)) { + // Allow longer resources strings to be specified as a list of strings, which represent multiple lines + s = s.join("\n"); } /*All args passed to current function: you can call getString('foo', 'bar', 'baz') or getString('foo',['bar', 'baz']) and the utility function will extract ['bar', 'baz'] as args in both cases*/ - var args = this.getOptionalArgsArrayFromFunctionCall(arguments, 1); + var args = ResourcesManager.getOptionalArgsArrayFromFunctionCall(arguments, 2); if (args) { for (var i: number = 0; i < args.length; i++) { s = s.replace("{" + i + "}", args[i]); @@ -43,39 +75,146 @@ module TacoUtility { return s; } - public static bestLanguageMatchOrDefault(language: string, resourcesDir: string): string { - if (!language) { - return this.DefaultLanguage; + private static bestLanguageMatchOrDefault(requestOrAcceptLangs: any): string { + var lang = ResourcesManager.bestLanguageMatch(requestOrAcceptLangs); + if (!ResourcesManager.Resources[lang]) { + lang = ResourcesManager.DefaultLanguage; } - var supportedLanguages: string[] = []; - fs.readdirSync(resourcesDir).filter(function (c: string): boolean { - return fs.existsSync(path.join(resourcesDir, c, "resources.json")); - }).forEach(function (l: string): void { - supportedLanguages.push(l.toLowerCase()); - }); - - // TODO: remove assumption of case insensitive file system, so this can work on non-windows systems. - var lang = language.toLowerCase(); - if (supportedLanguages.indexOf(lang) !== -1) { - // exact match on language, which could include the region (e.g. it-CH), return the match - return lang; - } - - var primaryLang = lang.split("-")[0]; - if (supportedLanguages.indexOf(primaryLang) !== -1) { - // match on primary language (e.g. it from it-CH). - return primaryLang; - } - - return this.DefaultLanguage; + return lang.toLowerCase(); } - public static loadLanguage(language: string, resourcesDir: string): any { + /** + * requestOrAcceptLangs can either be: + * A string, with format "LangSpec[,LangSpec]*" where LangSpec is "Language[;anything]" + * e.g. "pl,fr-FR;q=0.3,en-US;q=0.1" is interpreted as "pl" or "fr-FR" or "en-US". Currently we ignore provided quality (q) values + * An array, which we assume is an array of strings representing languages such as "pl" or "fr-FR" + * A (express-style) HTTP Request object, with a headers property specifing "accept-language" in a string, as above + * + * This allows us to handle simple cases of a single string, as well as more complex cases where a client specifies + * multiple preferences. + */ + private static bestLanguageMatch(requestOrAcceptLangs: any): string { + if (Array.isArray(requestOrAcceptLangs)) { + return ResourcesManager.getBestLanguageFromArray(requestOrAcceptLangs); + } + + var langString: string; + if (!requestOrAcceptLangs) { + return ResourcesManager.DefaultLanguage; + } else if (typeof requestOrAcceptLangs === "string") { + langString = requestOrAcceptLangs; + } else if (requestOrAcceptLangs.headers) { + langString = requestOrAcceptLangs.headers["accept-language"] || ""; + } else { + throw new Error("Unsupported type of argument for acceptLangs: " + (typeof requestOrAcceptLangs)); + } + + return ResourcesManager.getBestLanguageFromArray(langString.split(",").map(function (l: string): string { return l.split(";")[0]; })); + } + + /** + * Given a list of languages, we try to find an exact match if we can, and we fall back to a primary language otherwise. + * e.g. "fr-CA" will use "fr-CA" resources if present, but will fall back to "fr" resources if they are available + */ + private static getBestLanguageFromArray(acceptLangs: string[]): string { + var primaryLanguageMatch: string = null; + for (var i: number = 0; i < acceptLangs.length; ++i) { + var lang: string = acceptLangs[i].toLowerCase(); + var primaryLang = lang.split("-")[0]; + if (ResourcesManager.SupportedLanguages.indexOf(lang) !== -1) { + // Exact match on full language header, which could include the region + return lang; + } + + if (ResourcesManager.SupportedLanguages.indexOf(primaryLang) !== -1) { + // Match on primary language (e.g. it from it-CH). We may find a better match later, so continue looking. + primaryLanguageMatch = primaryLang; + } + } + + return primaryLanguageMatch; + } + + private static loadLanguage(language: string, resourcesDir: string): any { var resourcesPath = path.join(resourcesDir, language, "resources.json"); return require(resourcesPath); } + private static getOptionalArgsArrayFromFunctionCall(functionArguments: IArguments, startFrom: number): any[] { + if (functionArguments.length <= startFrom) { + return null; + } + + if (Array.isArray(functionArguments[startFrom])) { + return functionArguments[startFrom]; + } + + return Array.prototype.slice.apply(functionArguments, [startFrom]); + } + } + + export class CordovaConfig { + /** CordovaConfig is a class for parsing the config.xml file for Cordova projects */ + private _doc: et.ElementTree; + + constructor(configXmlPath: string) { + var contents = UtilHelper.readFileContentsSync(configXmlPath, "utf-8"); + this._doc = new et.ElementTree(et.XML(contents)); + } + + public id(): string { + return this._doc.getroot().attrib["id"]; + } + + public name(): string { + var el = this._doc.find("name"); + return el && el.text && el.text.trim().normalize(); + } + + public version(): string { + var el = this._doc.getroot(); + return el && el.attrib && el.attrib["version"]; + } + + public preferences(): { [key: string]: string } { + var data: { [key: string]: string } = {}; + var preferences = this._doc.findall("preference"); + preferences.forEach(function (preference: et.XMLElement): void { + data[preference.attrib["name"]] = preference.attrib["value"]; + }); + + return data; + } + } + + export class UtilHelper { + private static InvalidAppNameChars = { + 34: "\"", + 36: "$", + 38: "&", + 39: "/", + 60: "<", + 92: "\\" + }; + /** Converts an untyped argument into a boolean in a "sensible" way, treating only the string "true" as true rather than any non-empty string * */ + public static argToBool(input: any): boolean { + if (typeof input === "string") { + return input.toLowerCase() === "true"; + } + + return !!input; + } + + public static readFileContentsSync(filename: string, encoding?: string): string { + var contents = fs.readFileSync(filename, (encoding || "utf-8")); + if (contents) { + contents = contents.replace(/^\uFEFF/, ""); // Windows is the BOM + } + + return contents; + } + public static getOptionalArgsArrayFromFunctionCall(functionArguments: IArguments, startFrom: number): any[] { if (functionArguments.length <= startFrom) { return null; @@ -88,6 +227,86 @@ module TacoUtility { return Array.prototype.slice.apply(functionArguments, [startFrom]); } } + + export class BuildInfo { + public status: string; + /** + * BuildInfo holds relevant information about a particular build, including its identifier, what kind of build was requested, and the status of the build + * This information is passed around both within the remote build server, and back to the client + */ + public changeList: { + deletedFiles: string[]; + changedFiles: string[]; + addedPlugins: string[]; + deletedPlugins: string[]; + deletedFilesIos: string[]; + changedFilesIos: string[]; + addedPluginsIos: string[]; + deletedPluginsIos: string[]; + }; + public statusTime: Date; + public cordovaVersion: string; + public buildCommand: string; + public configuration: string; + public options: any; + public buildDir: string; + public buildLang: string; + public buildPlatform: string; + public submissionTime: Date; + public buildNumber: number; + + public messageId: string; + public messageArgs: any[]; + public message: string; + + public tgzFilePath: string; + public appDir: string; + public appName: string; + + public webDebugProxyPort: number; + + constructor(params: { buildNumber?: number; status?: string; cordovaVersion?: string; buildCommand?: string; configuration?: string; options?: any; buildDir?: string; buildLang?: string; buildPlatform?: string }) { + this.buildNumber = params.buildNumber; + this.status = params.status; + this.cordovaVersion = params.cordovaVersion; + this.buildCommand = params.buildCommand; + this.configuration = params.configuration; + this.options = params.options; + this.buildDir = params.buildDir; + this.buildLang = params.buildLang; + this.buildPlatform = params.buildPlatform; + this.submissionTime = new Date(); + this.changeList = null; + } + + public static createNewBuildInfoFromDataObject(buildInfoData: any): BuildInfo { + var bi: any = new BuildInfo(buildInfoData); + Object.keys(buildInfoData).forEach(function (k: string): void { + bi[k] = buildInfoData[k]; + }); + return bi; + } + + public updateStatus(status: string, messageId?: string, ...messageArgs: any[]): void { + this.status = status; + this.messageId = messageId; + if (arguments.length > 2) { + this.messageArgs = UtilHelper.getOptionalArgsArrayFromFunctionCall(arguments, 2); + } + + this.statusTime = new Date(); + } + + public localize(req: any): BuildInfo { + if (this.messageId) { + this.message = ResourcesManager.getStringForLanguage(req, this.messageId, this.messageArgs); + } else { + this.message = ResourcesManager.getStringForLanguage(req, "Build-" + this.status); + } + + return this; + } + } } export = TacoUtility; \ No newline at end of file diff --git a/src/taco-utils/test/cordova-config.ts b/src/taco-utils/test/cordova-config.ts new file mode 100644 index 00000000..9d22c4d6 --- /dev/null +++ b/src/taco-utils/test/cordova-config.ts @@ -0,0 +1,35 @@ +/** + * ****************************************************** + * * + * Copyright (C) Microsoft. All rights reserved. * + * * + ******************************************************* + */ +"use strict"; +/// +/// +import should = require ("should"); +import mocha = require ("mocha"); + +import fs = require ("fs"); +import path = require ("path"); + +import util = require ("../taco-utils"); + +/// +var CordovaConfig = util.CordovaConfig; +/// + +describe("CordovaConfig", function (): void { + it("should correctly parse config files", function (): void { + var cfg = new CordovaConfig(path.join(__dirname, "resources", "config.xml")); + cfg.id().should.equal("org.foo.bar"); + cfg.name().should.equal("FooBar"); + cfg.version().should.equal("0.9.2"); + }); + + it("should correctly normalize unicode display names", function (): void { + var cfg = new CordovaConfig(path.join(__dirname, "resources", "config_unicode.xml")); + cfg.name().should.equal("隣兀﨩"); // Note that this is NOT identical to the name in config_unicode, it is the normalized form. + }); +}); \ No newline at end of file diff --git a/src/taco-utils/test/resources.ts b/src/taco-utils/test/resources.ts new file mode 100644 index 00000000..ca2d4709 --- /dev/null +++ b/src/taco-utils/test/resources.ts @@ -0,0 +1,94 @@ +/** + * ****************************************************** + * * + * Copyright (C) Microsoft. All rights reserved. * + * * + ******************************************************* + */ +/// +/// +/// +"use strict"; +import path = require ("path"); +import should = require ("should"); +import mocha = require ("mocha"); + +import util = require ("../taco-utils"); + +var resources = util.ResourcesManager; + +describe("resources", function (): void { + before(function (): void { + resources.init("en", path.join(__dirname, "resources")); + }); + after(function (): void { + resources.teardown(); + }); + + it("should use the default language by default", function (): void { + var expectedResources = require(path.join(__dirname, "/resources/en/resources.json")); + var actual = resources.getString("SimpleMessage"); + var expected = expectedResources["SimpleMessage"]; + actual.should.equal(expected); + }); + + it("should correctly return strings for a primary language", function (): void { + var expectedResources = require(path.join(__dirname, "/resources/en/resources.json")); + var actual = resources.getStringForLanguage("en", "SimpleMessage"); + var expected = expectedResources["SimpleMessage"]; + actual.should.equal(expected); + }); + + it("should correctly return strings for a non-english primary language", function (): void { + var expectedResources = require(path.join(__dirname, "/resources/it/resources.json")); + var actual = resources.getStringForLanguage("it", "SimpleMessage"); + var expected = expectedResources["SimpleMessage"]; + actual.should.equal(expected); + }); + + it("should correctly substitute arguments in resource strings", function (): void { + var expectedResources = require(path.join(__dirname, "/resources/en/resources.json")); + var actual = resources.getStringForLanguage("en", "MessageWithArgs", "Billy", "Fish"); + var expected = expectedResources["MessageWithArgs"]; + var actualBackToExpected = actual.replace("Billy", "{0}").replace("Fish", "{1}"); + actualBackToExpected.should.equal(expected); + + var actualWithArrayArgs = resources.getStringForLanguage("en", "MessageWithArgs", ["Billy", "Fish"]); + actualWithArrayArgs.should.equal(actual); + }); + + it("should find the most specific region for a language", function (): void { + var expectedResources = require(path.join(__dirname, "/resources/it-ch/resources.json")); + var actual = resources.getStringForLanguage("it-ch", "SimpleMessage"); + var expected = expectedResources["SimpleMessage"]; + actual.should.equal(expected); + }); + + it("should fall back to more general languages if a more specific one is not available", function (): void { + var expectedResources = require(path.join(__dirname, "/resources/it/resources.json")); + var actual = resources.getStringForLanguage("it-DE", "SimpleMessage"); + var expected = expectedResources["SimpleMessage"]; + actual.should.equal(expected); + }); + + it("should fall back to english as a default if the language is unknown", function (): void { + var expectedResources = require(path.join(__dirname, "/resources/en/resources.json")); + var actual = resources.getStringForLanguage("hy-Latn-IT-arevela", "SimpleMessage"); + var expected = expectedResources["SimpleMessage"]; + actual.should.equal(expected); + }); + + it("should return undefined for bad resource identifiers", function (): void { + var actual = resources.getStringForLanguage("en", "NoResourceDefinedForThis"); + /// + should(actual).be.equal(undefined); + /// + }); + + it("should handle unicode in resource strings", function (): void { + var expectedResources = require(path.join(__dirname, "/resources/gb18030/resources.json")); + var actual = resources.getStringForLanguage("gb18030", "UnicodeMessage"); + var expected = expectedResources["UnicodeMessage"]; + actual.should.equal(expected); + }); +}); \ No newline at end of file diff --git a/src/taco-utils/test/resources/config.xml b/src/taco-utils/test/resources/config.xml new file mode 100644 index 00000000..a320aff3 --- /dev/null +++ b/src/taco-utils/test/resources/config.xml @@ -0,0 +1,16 @@ + + + FooBar + + A sample Apache Cordova application that responds to the deviceready event. + + + Apache Cordova Team + + + + + + diff --git a/src/taco-utils/test/resources/config_unicode.xml b/src/taco-utils/test/resources/config_unicode.xml new file mode 100644 index 00000000..d8d15061 --- /dev/null +++ b/src/taco-utils/test/resources/config_unicode.xml @@ -0,0 +1,16 @@ + + + 隣兀﨩 + + A sample Apache Cordova application that responds to the deviceready event. + + + Apache Cordova Team + + + + + + diff --git a/src/taco-utils/test/resources/en/resources.json b/src/taco-utils/test/resources/en/resources.json new file mode 100644 index 00000000..e9829718 --- /dev/null +++ b/src/taco-utils/test/resources/en/resources.json @@ -0,0 +1,4 @@ +{ + "SimpleMessage": "Hello", + "MessageWithArgs": "Hello {0} {1}" +} \ No newline at end of file diff --git a/src/taco-utils/test/resources/gb18030/resources.json b/src/taco-utils/test/resources/gb18030/resources.json new file mode 100644 index 00000000..fb2782e3 --- /dev/null +++ b/src/taco-utils/test/resources/gb18030/resources.json @@ -0,0 +1,3 @@ +{ + "UnicodeMessage": "啊齄丂狛狜隣郎隣兀﨩ˊ▇█〞〡¦℡㈱‐ー﹡﹢﹫、〓ⅰⅹ⒈€㈠㈩ⅠⅫ! ̄ぁんァヶΑ︴АЯаяāɡㄅㄩ─╋︵﹄︻︱︳︴ⅰⅹɑɡ〇〾⿻⺁䜣€龹龻㐀㒣㕴㕵㙉㙊䵯䵰䶴䶵𠀀𠀁𠀂𠀃𪛑𪛒𪛓𪛔𪛕𪛖" +} \ No newline at end of file diff --git a/src/taco-utils/test/resources/it-ch/resources.json b/src/taco-utils/test/resources/it-ch/resources.json new file mode 100644 index 00000000..a5d1c3f8 --- /dev/null +++ b/src/taco-utils/test/resources/it-ch/resources.json @@ -0,0 +1,4 @@ +{ + "SimpleMessage": "Ciao it-CH", + "MessageWithArgs": "Ciao {0} {1} it-CH" +} \ No newline at end of file diff --git a/src/taco-utils/test/resources/it/resources.json b/src/taco-utils/test/resources/it/resources.json new file mode 100644 index 00000000..9b696776 --- /dev/null +++ b/src/taco-utils/test/resources/it/resources.json @@ -0,0 +1,4 @@ +{ + "SimpleMessage": "Ciao", + "MessageWithArgs": "Ciao {0} {1}" +} \ No newline at end of file diff --git a/src/typings/elementtree.d.ts b/src/typings/elementtree.d.ts new file mode 100644 index 00000000..79cf52b2 --- /dev/null +++ b/src/typings/elementtree.d.ts @@ -0,0 +1,18 @@ +// Barebones typing for elementtree, added as-needed + +declare module "elementtree" { + export class ElementTree { + constructor(xml: XMLElement); + + getroot(): XMLElement + find(name: string): XMLElement; + findall(name: string): XMLElement[]; + } + + export class XMLElement { + attrib: { [key: string]: string }; + text: string; + } + + export function XML(data: string): XMLElement; +} \ No newline at end of file diff --git a/src/typings/mocha.d.ts b/src/typings/mocha.d.ts new file mode 100644 index 00000000..30f02f6d --- /dev/null +++ b/src/typings/mocha.d.ts @@ -0,0 +1,114 @@ +// Type definitions for mocha 1.17.1 +// Project: http://visionmedia.github.io/mocha/ +// Definitions by: Kazi Manzur Rashid , otiai10 +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +interface Mocha { + // Setup mocha with the given setting options. + setup(options: MochaSetupOptions): Mocha; + + //Run tests and invoke `fn()` when complete. + run(callback?: () => void): void; + + // Set reporter as function + reporter(reporter: () => void): Mocha; + + // Set reporter, defaults to "dot" + reporter(reporter: string): Mocha; + + // Enable growl support. + growl(): Mocha +} + +interface MochaSetupOptions { + //milliseconds to wait before considering a test slow + slow?: number; + + // timeout in milliseconds + timeout?: number; + + // ui name "bdd", "tdd", "exports" etc + ui?: string; + + //array of accepted globals + globals?: any[]; + + // reporter instance (function or string), defaults to `mocha.reporters.Dot` + reporter?: any; + + // bail on the first test failure + bail?: Boolean; + + // ignore global leaks + ignoreLeaks?: Boolean; + + // grep string or regexp to filter tests with + grep?: any; +} + +interface MochaDone { + (error?: Error): void; +} + +declare var mocha: Mocha; + +declare var describe: { + (description: string, spec: () => void): void; + only(description: string, spec: () => void): void; + skip(description: string, spec: () => void): void; + timeout(ms: number): void; +} + +// alias for `describe` +declare var context: { + (contextTitle: string, spec: () => void): void; + only(contextTitle: string, spec: () => void): void; + skip(contextTitle: string, spec: () => void): void; + timeout(ms: number): void; +} + +declare var it: { + (expectation: string, assertion?: () => void): void; + (expectation: string, assertion?: (done: MochaDone) => void): void; + only(expectation: string, assertion?: () => void): void; + only(expectation: string, assertion?: (done: MochaDone) => void): void; + skip(expectation: string, assertion?: () => void): void; + skip(expectation: string, assertion?: (done: MochaDone) => void): void; + timeout(ms: number): void; +}; + +declare function before(action: () => void): void; + +declare function before(action: (done: MochaDone) => void): void; + +declare function setup(action: () => void): void; + +declare function setup(action: (done: MochaDone) => void): void; + +declare function after(action: () => void): void; + +declare function after(action: (done: MochaDone) => void): void; + +declare function teardown(action: () => void): void; + +declare function teardown(action: (done: MochaDone) => void): void; + +declare function beforeEach(action: () => void): void; + +declare function beforeEach(action: (done: MochaDone) => void): void; + +declare function suiteSetup(action: () => void): void; + +declare function suiteSetup(action: (done: MochaDone) => void): void; + +declare function afterEach(action: () => void): void; + +declare function afterEach(action: (done: MochaDone) => void): void; + +declare function suiteTeardown(action: () => void): void; + +declare function suiteTeardown(action: (done: MochaDone) => void): void; + +declare module "mocha" { + export = undefined; +} \ No newline at end of file diff --git a/src/typings/should.d.ts b/src/typings/should.d.ts new file mode 100644 index 00000000..e9af28df --- /dev/null +++ b/src/typings/should.d.ts @@ -0,0 +1,122 @@ +// Type definitions for should.js 3.1.2 +// Project: https://github.com/visionmedia/should.js +// Definitions by: Alex Varju , Maxime LUCE +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +interface Object { + should: ShouldAssertion; +} + +interface ShouldAssertion { + // basic grammar + a: ShouldAssertion; + an: ShouldAssertion; + and: ShouldAssertion; + be: ShouldAssertion; + have: ShouldAssertion; + with: ShouldAssertion; + of: ShouldAssertion; + not: ShouldAssertion; + + // validators + arguments: ShouldAssertion; + empty: ShouldAssertion; + ok: ShouldAssertion; + true: ShouldAssertion; + false: ShouldAssertion; + NaN: ShouldAssertion; + Infinity: ShouldAssertion; + Array: ShouldAssertion; + Object: ShouldAssertion; + String: ShouldAssertion; + Boolean: ShouldAssertion; + Number: ShouldAssertion; + Error: ShouldAssertion; + Function: ShouldAssertion; + eql(expected: any, description?: string): ShouldAssertion; + equal(expected: any, description?: string): ShouldAssertion; + within(start: number, finish: number, description?: string): ShouldAssertion; + approximately(value: number, delta: number, description?: string): ShouldAssertion; + type(expected: any, description?: string): ShouldAssertion; + instanceof(constructor: Function, description?: string): ShouldAssertion; + above(n: number, description?: string): ShouldAssertion; + below(n: number, description?: string): ShouldAssertion; + match(other: {}, description?: string): ShouldAssertion; + match(other: (val: any) => any, description?: string): ShouldAssertion; + match(regexp: RegExp, description?: string): ShouldAssertion; + match(other: any, description?: string): ShouldAssertion; + matchEach(other: {}, description?: string): ShouldAssertion; + matchEach(other: (val: any) => any, description?: string): ShouldAssertion; + matchEach(regexp: RegExp, description?: string): ShouldAssertion; + matchEach(other: any, description?: string): ShouldAssertion; + length(n: number, description?: string): ShouldAssertion; + property(name: string, description?: string): ShouldAssertion; + property(name: string, val: any, description?: string): ShouldAssertion; + properties(names: string[]): ShouldAssertion; + properties(name: string): ShouldAssertion; + properties(descriptor: any): ShouldAssertion; + properties(...properties: string[]): ShouldAssertion; + ownProperty(name: string, description?: string): ShouldAssertion; + contain(obj: any): ShouldAssertion; + containEql(obj: any): ShouldAssertion; + containDeep(obj: any): ShouldAssertion; + keys(...allKeys: string[]): ShouldAssertion; + keys(allKeys: string[]): ShouldAssertion; + header(field: string, val?: string): ShouldAssertion; + status(code: number): ShouldAssertion; + json: ShouldAssertion; + html: ShouldAssertion; + startWith(expected: string, message?: any): ShouldAssertion; + endWith(expected: string, message?: any): ShouldAssertion; + throw(message?: any): ShouldAssertion; + + // deprecated + include(obj: any, description?: string): ShouldAssertion; + includeEql(obj: any[], description?: string): ShouldAssertion; + + // aliases + exactly(expected: any, description?: string): ShouldAssertion; + instanceOf(constructor: Function, description?: string): ShouldAssertion; + throwError(message?: any): ShouldAssertion; + lengthOf(n: number, description?: string): ShouldAssertion; + key(key: string): ShouldAssertion; + haveOwnProperty(name: string, description?: string): ShouldAssertion; + greaterThan(n: number, description?: string): ShouldAssertion; + lessThan(n: number, description?: string): ShouldAssertion; +} + +interface ShouldInternal { + // should.js's extras + exist(actual: any, msg?: string): void; + exists(actual: any, msg?: string): void; + not: ShouldInternal; +} + +interface Internal extends ShouldInternal { + (obj: any): ShouldAssertion; + + // node.js's assert functions + fail(actual: any, expected: any, message: string, operator: string): void; + assert(value: any, message: string): void; + ok(value: any, message?: string): void; + equal(actual: any, expected: any, message?: string): void; + notEqual(actual: any, expected: any, message?: string): void; + deepEqual(actual: any, expected: any, message?: string): void; + notDeepEqual(actual: any, expected: any, message?: string): void; + strictEqual(actual: any, expected: any, message?: string): void; + notStrictEqual(actual: any, expected: any, message?: string): void; + throws(block: any, error?: any, message?: string): void; + doesNotThrow(block: any, message?: string): void; + ifError(value: any): void; + inspect(value: any, obj: any): any; +} + +declare var should: Internal; +declare var Should: Internal; +interface Window { + Should: Internal; +} + +declare module "should" { + export = should; +} diff --git a/src/typings/taco-utils.d.ts b/src/typings/taco-utils.d.ts index 58890253..35ff932c 100644 --- a/src/typings/taco-utils.d.ts +++ b/src/typings/taco-utils.d.ts @@ -1,14 +1,103 @@ /// +/// +/// declare module TacoUtility { class ResourcesManager { private static Resources; + private static SupportedLanguages; private static DefaultLanguage; static init(language: string, resourcesDir?: string): void; + static teardown(): void; + /** ...optionalArgs is only there for typings, function rest params */ static getString(id: string, ...optionalArgs: any[]): string; - static bestLanguageMatchOrDefault(language: string, resourcesDir: string): string; - static loadLanguage(language: string, resourcesDir: string): any; + /** ** ...optionalArgs is only there for typings, function rest params** */ + static getStringForLanguage(requestOrAcceptLangs: any, id: string, ...optionalArgs: any[]): string; + private static bestLanguageMatchOrDefault(requestOrAcceptLangs); + /** + * requestOrAcceptLangs can either be: + * A string, with format "LangSpec[,LangSpec]*" where LangSpec is "Language[;anything]" + * e.g. "pl,fr-FR;q=0.3,en-US;q=0.1" is interpreted as "pl" or "fr-FR" or "en-US". Currently we ignore provided quality (q) values + * An array, which we assume is an array of strings representing languages such as "pl" or "fr-FR" + * A (express-style) HTTP Request object, with a headers property specifing "accept-language" in a string, as above + * + * This allows us to handle simple cases of a single string, as well as more complex cases where a client specifies + * multiple preferences. + */ + private static bestLanguageMatch(requestOrAcceptLangs); + /** + * Given a list of languages, we try to find an exact match if we can, and we fall back to a primary language otherwise. + * e.g. "fr-CA" will use "fr-CA" resources if present, but will fall back to "fr" resources if they are available + */ + private static getBestLanguageFromArray(acceptLangs); + private static loadLanguage(language, resourcesDir); + private static getOptionalArgsArrayFromFunctionCall(functionArguments, startFrom); + } + class CordovaConfig { + /** CordovaConfig is a class for parsing the config.xml file for Cordova projects */ + private _doc; + constructor(configXmlPath: string); + id(): string; + name(): string; + version(): string; + preferences(): { + [key: string]: string; + }; + } + class UtilHelper { + private static InvalidAppNameChars; + /** Converts an untyped argument into a boolean in a "sensible" way, treating only the string "true" as true rather than any non-empty string * */ + static argToBool(input: any): boolean; + static readFileContentsSync(filename: string, encoding?: string): string; static getOptionalArgsArrayFromFunctionCall(functionArguments: IArguments, startFrom: number): any[]; } + class BuildInfo { + status: string; + /** + * BuildInfo holds relevant information about a particular build, including its identifier, what kind of build was requested, and the status of the build + * This information is passed around both within the remote build server, and back to the client + */ + changeList: { + deletedFiles: string[]; + changedFiles: string[]; + addedPlugins: string[]; + deletedPlugins: string[]; + deletedFilesIos: string[]; + changedFilesIos: string[]; + addedPluginsIos: string[]; + deletedPluginsIos: string[]; + }; + statusTime: Date; + cordovaVersion: string; + buildCommand: string; + configuration: string; + options: any; + buildDir: string; + buildLang: string; + buildPlatform: string; + submissionTime: Date; + buildNumber: number; + messageId: string; + messageArgs: any[]; + message: string; + tgzFilePath: string; + appDir: string; + appName: string; + webDebugProxyPort: number; + constructor(params: { + buildNumber?: number; + status?: string; + cordovaVersion?: string; + buildCommand?: string; + configuration?: string; + options?: any; + buildDir?: string; + buildLang?: string; + buildPlatform?: string; + }); + static createNewBuildInfoFromDataObject(buildInfoData: any): BuildInfo; + updateStatus(status: string, messageId?: string, ...messageArgs: any[]): void; + localize(req: any): BuildInfo; + } } declare module "taco-utils"{ export = TacoUtility; diff --git a/src/typings/unorm.d.ts b/src/typings/unorm.d.ts new file mode 100644 index 00000000..153d1ea7 --- /dev/null +++ b/src/typings/unorm.d.ts @@ -0,0 +1,12 @@ +// Barebones typing for unorm, added as-needed + +interface Object { + normalize: (form?: string) => string; +} + +declare module "unorm" { + export function nfc(str: string): string; + export function nfd(str: string): string; + export function nfkc(str: string): string; + export function nfkd(str: string): string; +} \ No newline at end of file