Open parzhitsky opened 3 years ago
I need a function that takes a path to json and returns a complete config object.
Just a heads up, fundamentally, you can't do this without a level of complexity approximately on par with what is already out there. tsconfig files can have extends
clauses that can refer to both filenames and module names, and resolving a module name to a file requires being able to probe for files. A similar thing is true for types
- the default behavior here involves directory enumeration.
You need a host
to provide this functionality because all our APIs are designed in a way that allow them to be completely decoupled from the physical file system (for scenarios like running in a browser)
I see. Well, crap. The demand is clearly there, but looks like it is incompatible with current design of TypeScript.
And we cannot even use configurable defaults (e.g., use fs.readdir
when applicable, otherwise ask for it to be provided, otherwise produce error), can we?
Is there any chance then to at least have a 2021 version of this snippet from 2016 by @mhegazy?
That sample still works today with surprisingly few tweaks:
import ts = require("typescript");
import fs = require("fs");
import path = require("path");
function reportDiagnostics(diagnostics: ts.Diagnostic[]): void {
diagnostics.forEach(diagnostic => {
let message = "Error";
if (diagnostic.file && diagnostic.start) {
let { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
message += ` ${diagnostic.file.fileName} (${line + 1},${character + 1})`;
}
message += ": " + ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
console.log(message);
});
}
function readConfigFile(configFileName: string) {
// Read config file
const configFileText = fs.readFileSync(configFileName).toString();
// Parse JSON, after removing comments. Just fancier JSON.parse
const result = ts.parseConfigFileTextToJson(configFileName, configFileText);
const configObject = result.config;
if (!configObject) {
reportDiagnostics([result.error!]);
process.exit(1);;
}
// Extract config infromation
const configParseResult = ts.parseJsonConfigFileContent(configObject, ts.sys, path.dirname(configFileName));
if (configParseResult.errors.length > 0) {
reportDiagnostics(configParseResult.errors);
process.exit(1);
}
return configParseResult;
}
function compile(configFileName: string): void {
// Extract configuration from config file
let config = readConfigFile(configFileName);
// Compile
let program = ts.createProgram(config.fileNames, config.options);
let emitResult = program.emit();
// Report errors
reportDiagnostics(ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics));
// Return code
let exitCode = emitResult.emitSkipped ? 1 : 0;
process.exit(exitCode);
}
compile(process.argv[2]);
@parzhitsky I would suggest looking at https://www.npmjs.com/package/@ts-morph/bootstrap. TypeScript needs a high level of flexibility and abstractness in its API so it can run anywhere, but nothing prevents someone from wrapping it with Node.js assumptions like @ts-morph/bootstrap.
some of you may be interested in tsconfck
import { parse } from 'tsconfck';
const {
tsconfigFile, // full path to found tsconfig
tsconfig, // tsconfig object including merged values from extended configs
extended, // separate unmerged results of all tsconfig files that contributed to tsconfig
solution, // solution result if tsconfig is part of a solution
referenced // referenced tsconfig results if tsconfig is a solution
} = await parse('foo/bar.ts');
It also offers a similar parseNative
function, which uses the typescript functions mentioned above
And last but not least a cli wrapper for simple checks
# print tsconfig for foo/bar.ts to stdout
npx tsconfck parse foo/bar.ts
Suggestion
Dedicated public beginner-friendly API (ideally, an asynchronous function on the
ts
namespace) for parsingtsconfig.json
/jsconfig.json
files into a complete config object.🔍 Search Terms
parse JSON config tsconfig tsconfig.json jsconfig.json
✅ Viability Checklist
My suggestion meets these guidelines:
⭐ Suggestion
The suggestion is to have a single function that in its simplest form (overload) would take a string, – path to the config file (either
tsconfig.json
orjsconfig.json
, shouldn't matter), and return (either synchronously or asynchronously) the fully compiled complete configuration object with all the properties initialized (either explicitly from the provided config(s) or implicitly, from known defaults).The suggestion is related to issue #44516 and this StackOverflow question.
📃 Motivating Example
I know that there are already a couple of functions on the imported
ts
namespace whose purpose is to read/parse/compile config, given its filepath/stringified contents/parsed contents. These include:ts.readConfigFile
ts.parseConfigFileTextToJson
ts.readJsonConfigFile
ts.parseJsonConfigFileContent
However, they either provide incomplete functionality (e.g.,
ts.readConfigFile
is just a glorifiedJSON.parse
, it doesn't – for example – crawl parent configs) or are ridiculously complicated to use (e.g.,ts.parseConfigFileTextToJson
require 3-9 arguments, the third of which is some kind ofhost
object, that has to have areadDirectory
method, that in turn requires 4-5 arguments, and has to actually do something, which I honestly don't know why I can't just usefs.readdirSync
).I need a function that takes a path to json and returns a complete config object.
💻 Use Cases
I'm thinking of a couple of variants of the function (for the sake of an example, it is called
readConfig
):Also, shout out to functions that return
{ config?: Config; error?: unknown }
, which, however, are not idiomatic to Node.JS.