Add support for list types

consolidate typings options and commmandline option parsing from json files

Fix
This commit is contained in:
Mohamed Hegazy
2016-03-10 11:14:29 -08:00
committed by Kanchalai Tanglertsampan
parent 3adab0cec3
commit 0735f465f0
2 changed files with 149 additions and 135 deletions

View File

@@ -186,8 +186,8 @@ namespace ts {
name: "rootDir",
type: "string",
isFilePath: true,
description: Diagnostics.Specifies_the_root_directory_of_input_files_Use_to_control_the_output_directory_structure_with_outDir,
paramType: Diagnostics.LOCATION,
description: Diagnostics.Specifies_the_root_directory_of_input_files_Use_to_control_the_output_directory_structure_with_outDir,
},
{
name: "isolatedModules",
@@ -268,14 +268,17 @@ namespace ts {
error: Diagnostics.Argument_for_moduleResolution_option_must_be_node_or_classic,
},
{
name: "list",
elementType: {
"node": ModuleResolutionKind.NodeJs,
"classic": ModuleResolutionKind.Classic,
},
name: "lib",
type: "list",
element: {
name: "lib",
type: {
"node": ModuleResolutionKind.NodeJs,
"classic": ModuleResolutionKind.Classic,
},
error: Diagnostics.Argument_for_moduleResolution_option_must_be_node_or_classic,
},
description: Diagnostics.Specifies_module_resolution_strategy_Colon_node_Node_js_or_classic_TypeScript_pre_1_6,
error: Diagnostics.Argument_for_moduleResolution_option_must_be_node_or_classic,
},
{
name: "allowUnusedLabels",
@@ -319,9 +322,13 @@ namespace ts {
// this option can only be specified in tsconfig.json
// use type = object to copy the value as-is
name: "rootDirs",
type: "object",
type: "list",
isTSConfigOnly: true,
isFilePath: true
element: {
name: "rootDirs",
type: "string",
isFilePath: true
}
},
{
name: "traceModuleResolution",
@@ -345,6 +352,30 @@ namespace ts {
}
];
/* @internal */
export let typingOptionDeclarations: CommandLineOption[] = [
{
name: "enableAutoDiscovery",
type: "boolean",
},
{
name: "include",
type: "list",
element: {
name: "include",
type: "string"
}
},
{
name: "exclude",
type: "list",
element: {
name: "include",
type: "string"
}
}
];
/* @internal */
export interface OptionNameMap {
optionNameMap: Map<CommandLineOption>;
@@ -401,7 +432,40 @@ namespace ts {
}
if (hasProperty(optionNameMap, s)) {
parseString(optionNameMap[s], args[i]);
const opt = optionNameMap[s];
if (opt.isTSConfigOnly) {
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file, opt.name));
}
else {
// Check to see if no argument was provided (e.g. "--locale" is the last command-line argument).
if (!args[i] && opt.type !== "boolean") {
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_expects_an_argument, opt.name));
}
switch (opt.type) {
case "number":
options[opt.name] = parseInt(args[i]);
i++;
break;
case "boolean":
options[opt.name] = true;
break;
case "string":
options[opt.name] = args[i] || "";
i++;
break;
case "list":
options[opt.name] = parseListTypeOption(<CommandLineOptionOfListType>opt, args[i]);
i++;
break;
// If not a primitive, the possible types are specified in what is effectively a map of options.
default:
options[opt.name] = parseCustomTypeOption(<CommandLineOptionOfCustomType>opt, args[i]);
i++;
break;
}
}
}
else {
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_compiler_option_0, s));
@@ -410,40 +474,25 @@ namespace ts {
else {
fileNames.push(s);
}
}
function parseString(opt: CommandLineOption, value: string) {
// Check to see if no argument was provided (e.g. "--locale" is the last command-line argument).
if (!value && opt.type !== "boolean") {
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_expects_an_argument, opt.name));
function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value: string) {
const map = <Map<number>>opt.type;
const key = (value || "").toLowerCase();
if (hasProperty(map, key)) {
return map[key];
}
else {
errors.push(createCompilerDiagnostic(opt.error));
}
}
switch (opt.type) {
case "number":
options[opt.name] = parseInt(value);
i++;
break;
case "boolean":
options[opt.name] = true;
break;
case "string":
options[opt.name] = value || "";
i++;
break;
case "list":
forEach((value || "").split(","), s => parseString(opt.name, opti );
break;
// If not a primitive, the possible types are specified in what is effectively a map of options.
default:
let map = <Map<number>>opt.type;
let key = (value || "").toLowerCase();
i++;
if (hasProperty(map, key)) {
options[opt.name] = map[key];
}
else {
errors.push(createCompilerDiagnostic((<CommandLineOptionOfCustomType>opt).error));
}
function parseListTypeOption(opt: CommandLineOptionOfListType, value: string): number[] | string[] {
const values = (value || "").split(",");
switch (opt.element.type) {
case "number": return ts.map(values, parseInt);
case "string": return ts.map(values, v => v || "");
default: return ts.map(values, v => parseCustomTypeOption(<CommandLineOptionOfCustomType>opt.element, v));
}
}
}
}
@@ -512,7 +561,6 @@ namespace ts {
}
}
/**
* Remove the comments from a json like text.
* Comments can be single line comments (starting with # or //) or multiline comments using / * * /
@@ -546,18 +594,20 @@ namespace ts {
* file to. e.g. outDir
*/
export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string): ParsedCommandLine {
const { options: optionsFromJsonConfigFile, errors } = convertCompilerOptionsFromJson(json["compilerOptions"], basePath, configFileName);
const errors: Diagnostic[] = [];
const optionsFromJsonConfigFile = convertOptionsFromJson<CompilerOptions>(optionDeclarations, json["compilerOptions"], basePath, configFileName, errors);
const options = extend(existingOptions, optionsFromJsonConfigFile);
const typingOptions = convertOptionsFromJson<TypingOptions>(typingOptionDeclarations, json["typingOptions"], basePath, configFileName, errors);
const fileNames = getFileNames(errors);
return {
options,
fileNames: getFileNames(),
typingOptions: getTypingOptions(),
fileNames,
typingOptions,
errors
};
function getFileNames(): string[] {
function getFileNames(errors: Diagnostic[]): string[] {
let fileNames: string[] = [];
if (hasProperty(json, "files")) {
if (json["files"] instanceof Array) {
@@ -618,47 +668,24 @@ namespace ts {
}
return fileNames;
}
function getTypingOptions(): TypingOptions {
const options: TypingOptions = getBaseFileName(configFileName) === "jsconfig.json"
? { enableAutoDiscovery: true, include: [], exclude: [] }
: { enableAutoDiscovery: false, include: [], exclude: [] };
const jsonTypingOptions = json["typingOptions"];
if (jsonTypingOptions) {
for (const id in jsonTypingOptions) {
if (id === "enableAutoDiscovery") {
if (typeof jsonTypingOptions[id] === "boolean") {
options.enableAutoDiscovery = jsonTypingOptions[id];
}
else {
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_typing_option_0, id));
}
}
else if (id === "include") {
options.include = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors);
}
else if (id === "exclude") {
options.exclude = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors);
}
else {
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_typing_option_0, id));
}
}
}
return options;
}
}
export function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions, errors: Diagnostic[] } {
const options: CompilerOptions = {};
const errors: Diagnostic[] = [];
const options = convertOptionsFromJson<CompilerOptions>(optionDeclarations, jsonOptions, basePath, configFileName, errors);
if (configFileName && getBaseFileName(configFileName) === "jsconfig.json") {
if (configFileName && getBaseFileName(configFileName) === "jsconfig.json" && typeof options.allowJs === "undefined") {
options.allowJs = true;
}
return { options, errors };
}
function convertOptionsFromJson<T extends CompilerOptions | TypingOptions>(optionDeclarations: CommandLineOption[], jsonOptions: any, basePath: string, configFileName: string, errors: Diagnostic[]): T {
const options = {} as T;
if (!jsonOptions) {
return { options, errors };
return options;
}
const optionNameMap = arrayToMap(optionDeclarations, opt => opt.name);
@@ -666,69 +693,53 @@ namespace ts {
for (const id in jsonOptions) {
if (hasProperty(optionNameMap, id)) {
const opt = optionNameMap[id];
const optType = opt.type;
let value = jsonOptions[id];
const expectedType = typeof optType === "string" ? optType : "string";
if (typeof value === expectedType) {
if (typeof optType !== "string") {
const key = value.toLowerCase();
if (hasProperty(optType, key)) {
value = optType[key];
}
else {
errors.push(createCompilerDiagnostic((<CommandLineOptionOfCustomType>opt).error));
value = 0;
}
}
if (opt.isFilePath) {
switch (typeof value) {
case "string":
value = normalizePath(combinePaths(basePath, value));
break;
case "object":
// "object" options with 'isFilePath' = true expected to be string arrays
value = convertJsonOptionToStringArray(opt.name, value, errors, (element) => normalizePath(combinePaths(basePath, element)));
break;
}
if (value === "") {
value = ".";
}
}
options[opt.name] = value;
}
else {
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, id, expectedType));
}
options[opt.name] = convertJsonOption(opt, jsonOptions[id], basePath, errors);
}
else {
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_compiler_option_0, id));
}
}
return { options, errors };
return options;
}
function convertJsonOptionToStringArray(optionName: string, optionJson: any, errors: Diagnostic[], func?: (element: string) => string): string[] {
const items: string[] = [];
let invalidOptionType = false;
if (!isArray(optionJson)) {
invalidOptionType = true;
function convertJsonOption(opt: CommandLineOption, value: any, basePath: string, errors: Diagnostic[]): number | string | number[] | string[] {
const optType = opt.type;
const expectedType = typeof optType === "string" ? optType : "string";
if (optType === "list" && isArray(value)) {
return convertJsonOptionOfListType(<CommandLineOptionOfListType>opt, value, basePath, errors);
}
else {
for (const element of <any[]>optionJson) {
if (typeof element === "string") {
const item = func ? func(element) : element;
items.push(item);
}
else {
invalidOptionType = true;
break;
else if (typeof value === expectedType) {
if (typeof optType !== "string") {
return convertJsonOptionOfCustomType(<CommandLineOptionOfCustomType>opt, value, errors);
}
else {
if (opt.isFilePath) {
value = normalizePath(combinePaths(basePath, value));
if (value === "") {
value = ".";
}
}
}
return value;
}
if (invalidOptionType) {
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_should_have_array_of_strings_as_a_value, optionName));
else {
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, opt.name, expectedType));
}
return items;
}
function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) {
const key = value.toLowerCase();
if (hasProperty(opt.type, key)) {
return opt.type[key];
}
else {
errors.push(createCompilerDiagnostic(opt.error));
return 0;
}
}
function convertJsonOptionOfListType(option: CommandLineOptionOfListType, values: any[], basePath: string, errors: Diagnostic[]): any[] {
return ts.map(values, v => convertJsonOption(option.element, v, basePath, errors));
}
}

View File

@@ -2444,7 +2444,9 @@ namespace ts {
// Do not perform validation of output file name in transpile scenarios
/* @internal */ suppressOutputPathCheck?: boolean;
[option: string]: string | number | boolean | TsConfigOnlyOptions;
list?: string[];
[option: string]: string | number | boolean | TsConfigOnlyOptions | string[] | number[];
}
export interface TypingOptions {
@@ -2554,13 +2556,14 @@ namespace ts {
type: "object";
}
export interface CommandlineOptionOfListType extends CommandLineOptionBase {
type: "list",
elementType: Map<number> | "string" | "number";
/* @internal */
export interface CommandLineOptionOfListType extends CommandLineOptionBase {
type: "list";
element: CommandLineOptionOfCustomType | CommandLineOptionOfPrimitiveType;
}
/* @internal */
export type CommandLineOption = CommandLineOptionOfCustomType | CommandLineOptionOfPrimitiveType | TsConfigOnlyOption | CommandlineOptionOfListType;
export type CommandLineOption = CommandLineOptionOfCustomType | CommandLineOptionOfPrimitiveType | TsConfigOnlyOption | CommandLineOptionOfListType;
/* @internal */
export const enum CharacterCodes {