Intro
Because I want to know how to save the changes for the "incremental" option, I try to read the source code.They are in the TypeScript project.
GitHub - microsoft/TypeScript: TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
This time, I write about how to parse the tsc command from src > tsc > tsc.ts.
According to the tsconfig.json of tsc, it refers "compiler" and "executeCommandLine".
tsc/tsconfig.json
"references": [
{ "path": "../compiler", "prepend": true },
{ "path": "../executeCommandLine", "prepend": true }
]
tsc.ts and sys.ts
https://github.com/microsoft/TypeScript/blob/master/src/tsc/tsc.tsIn this code, I think the most important for understanding how to parse the command is here.
tsc/tsc.ts
ts.executeCommandLine(ts.sys, ts.noop, ts.sys.args);
The first argument had system info of Node.js.https://github.com/microsoft/TypeScript/blob/master/src/compiler/sys.ts
The second one "ts.noop" does nothing as its name suggests.
compiler/core.ts
...
/** Does nothing. */
export function noop(_?: {} | null | undefined): void { }
...
command-line arguments
The last one is command-line arguments.It comes from "process.argv.slice(2)".
For example, when I execute the JavaScript code by Node.js...
sample.js
"use strict";
function call() {
console.log(process.argv);
}
call();
node dist/main.js -hello --worldI can get the result like below.
[ '/usr/bin/node', '/home/example/Documents/workspace/ts-commandline-sample/dist/main.js', '-hello', '--world' ]And by "slice(2)", I can get them(get from the third element to the end).
Array.prototype.slice() - JavaScript | MDN
[ '-hello', '--world' ]
executeCommandLine.ts
This class checks if the operation is for build or not, and (for this time) builds the scripts.compiler/executeCommandLine.ts
...
export function executeCommandLine(
system: System,
cb: ExecuteCommandLineCallbacks,
commandLineArgs: readonly string[],
maxNumberOfFilesToIterateForInvalidation?: number
) {
if (isBuild(commandLineArgs)) {
const { buildOptions, watchOptions, projects, errors } = parseBuildCommand(commandLineArgs.slice(1));
if (buildOptions.generateCpuProfile && system.enableCPUProfiler) {
...
}
else {
return performBuild(
system,
cb,
buildOptions,
watchOptions,
projects,
errors
);
}
}
...
isBuild
compiler/executeCommandLine.ts
...
export function isBuild(commandLineArgs: readonly string[]) {
if (commandLineArgs.length > 0 && commandLineArgs[0].charCodeAt(0) === CharacterCodes.minus) {
const firstOption = commandLineArgs[0].slice(commandLineArgs[0].charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase();
return firstOption === "build" || firstOption === "b";
}
return false;
}
...
This function split the first option with minus symbol(0x2D) and checks if the remaining charactors are "build" or "b" or not.So according to this, I can understand these 2 things.
- I can "tsc -b -w" but I can't "tsc -w -b" for building scripts.
- I also can "tsc --b" for building scripts.
Get build options for parsing other options
After understanding this operation is for building, tsc gets build options for parsing other options.compiler/executeCommandLine.ts
const { buildOptions, watchOptions, projects, errors } = parseBuildCommand(commandLineArgs.slice(1));
For preparing, tsc gets the lists of CommandLineOption include "incremental" and create Map.compiler/commandLineParser.ts
...
/* @internal */
export const commonOptionsWithBuild: CommandLineOption[] = [
...
{
name: "incremental",
shortName: "i",
type: "boolean",
category: Diagnostics.Basic_Options,
description: Diagnostics.Enable_incremental_compilation,
transpileOptionValue: undefined
},
...
Project References · TypeScriptcompiler/commandLineParser.ts
...
/*@internal*/
export function createOptionNameMap(optionDeclarations: readonly CommandLineOption[]): OptionsNameMap {
const optionsNameMap = createMap<CommandLineOption>();
const shortOptionNames = createMap<string>();
forEach(optionDeclarations, option => {
optionsNameMap.set(option.name.toLowerCase(), option);
if (option.shortName) {
shortOptionNames.set(option.shortName, option.name);
}
});
return { optionsNameMap, shortOptionNames };
}
...
The function of createMap just creates Map<T>.Parse build options
tsc gets the option value as same as checking if the operation is for building.compiler/commandLineParser.ts
/*@internal*/
export function parseCommandLineWorker(
diagnostics: ParseCommandLineWorkerDiagnostics,
commandLine: readonly string[],
readFile?: (path: string) => string | undefined) {
...
function parseStrings(args: readonly string[]) {
let i = 0;
while (i < args.length) {
const s = args[i];
i++;
if (s.charCodeAt(0) === CharacterCodes.at) {
parseResponseFile(s.slice(1));
}
else if (s.charCodeAt(0) === CharacterCodes.minus) {
const inputOptionName = s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1);
const opt = getOptionDeclarationFromName(diagnostics.getOptionsNameMap, inputOptionName, /*allowShort*/ true);
if (opt) {
i = parseOptionValue(args, i, diagnostics, opt, options, errors);
}
else {
...
After parsing the option values, tsc put them into "options".compiler/commandLineParser.ts
...
function parseOptionValue(
args: readonly string[],
i: number,
diagnostics: ParseCommandLineWorkerDiagnostics,
opt: CommandLineOption,
options: OptionsBase,
errors: Diagnostic[]
) {
if (opt.isTSConfigOnly) {
const optValue = args[i];
if (optValue === "null") {
options[opt.name] = undefined;
i++;
}
else if (opt.type === "boolean") {
if (optValue === "false") {
options[opt.name] = false;
i++;
}
else {
if (optValue === "true") i++;
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_false_or_null_on_command_line, opt.name));
}
}
else {
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_null_on_command_line, opt.name));
if (optValue && !startsWith(optValue, "-")) i++;
}
}
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.optionTypeMismatchDiagnostic, opt.name, getCompilerOptionValueTypeString(opt)));
}
if (args[i] !== "null") {
switch (opt.type) {
case "number":
options[opt.name] = parseInt(args[i]);
i++;
break;
case "boolean":
// boolean flag has optional value true, false, others
const optValue = args[i];
options[opt.name] = optValue !== "false";
// consume next argument as boolean flag value
if (optValue === "false" || optValue === "true") {
i++;
}
break;
...
}
}
else {
options[opt.name] = undefined;
i++;
}
}
return i;
}
...
Next time, I will read about performing build.
コメント
コメントを投稿