Comparing version 1.0.0-4 to 1.0.0-5
@@ -21,6 +21,17 @@ /** | ||
* @param {string} filename - path to a tsconfig.json or a .ts source file (absolute or relative to cwd) | ||
* @param {ParseOptions} options - options | ||
* @returns {Promise<ParseResult>} | ||
* @throws {ParseError} | ||
*/ | ||
declare function parse(filename: string): Promise<ParseResult>; | ||
declare function parse(filename: string, options?: ParseOptions): Promise<ParseResult>; | ||
interface ParseOptions { | ||
/** | ||
* optional cache map to speed up repeated parsing with multiple files | ||
* it is your own responsibility to clear the cache if tsconfig files change during its lifetime | ||
* cache keys are input `filename` and absolute paths to tsconfig.json files | ||
* | ||
* You must not modify cached values. | ||
*/ | ||
cache?: Map<string, ParseResult>; | ||
} | ||
interface ParseResult { | ||
@@ -67,6 +78,17 @@ /** | ||
* @param {string} filename - path to a tsconfig.json or a .ts source file (absolute or relative to cwd) | ||
* @param {ParseNativeOptions} options - options | ||
* @returns {Promise<ParseNativeResult>} | ||
* @throws {ParseNativeError} | ||
*/ | ||
declare function parseNative(filename: string): Promise<ParseNativeResult>; | ||
declare function parseNative(filename: string, options?: ParseNativeOptions): Promise<ParseNativeResult>; | ||
interface ParseNativeOptions { | ||
/** | ||
* optional cache map to speed up repeated parsing with multiple files | ||
* it is your own responsibility to clear the cache if tsconfig files change during its lifetime | ||
* cache keys are input `filename` and absolute paths to tsconfig.json files | ||
* | ||
* You must not modify cached values. | ||
*/ | ||
cache?: Map<string, ParseNativeResult>; | ||
} | ||
interface ParseNativeResult { | ||
@@ -73,0 +95,0 @@ /** |
@@ -222,5 +222,4 @@ var __defProp = Object.defineProperty; | ||
function resolveSolutionTSConfig(filename, result) { | ||
var _a; | ||
if ([".ts", ".tsx"].some((ext) => filename.endsWith(ext)) && !isIncluded(filename, result)) { | ||
const solutionTSConfig = (_a = result.referenced) == null ? void 0 : _a.find((referenced) => isIncluded(filename, referenced)); | ||
if (result.referenced && [".ts", ".tsx"].some((ext) => filename.endsWith(ext)) && !isIncluded(filename, result)) { | ||
const solutionTSConfig = result.referenced.find((referenced) => isIncluded(filename, referenced)); | ||
if (solutionTSConfig) { | ||
@@ -321,16 +320,33 @@ return __spreadProps(__spreadValues({}, solutionTSConfig), { | ||
// src/parse.ts | ||
async function parse(filename) { | ||
async function parse(filename, options) { | ||
const cache = options == null ? void 0 : options.cache; | ||
if (cache == null ? void 0 : cache.has(filename)) { | ||
return cache.get(filename); | ||
} | ||
const tsconfigFile = await resolveTSConfig(filename) || await find(filename); | ||
const result = await parseFile(tsconfigFile); | ||
await Promise.all([parseExtends(result), parseReferences(result)]); | ||
return resolveSolutionTSConfig(filename, result); | ||
let result; | ||
if (cache == null ? void 0 : cache.has(tsconfigFile)) { | ||
result = cache.get(tsconfigFile); | ||
} else { | ||
result = await parseFile(tsconfigFile, cache); | ||
await Promise.all([parseExtends(result, cache), parseReferences(result, cache)]); | ||
cache == null ? void 0 : cache.set(tsconfigFile, result); | ||
} | ||
result = resolveSolutionTSConfig(filename, result); | ||
cache == null ? void 0 : cache.set(filename, result); | ||
return result; | ||
} | ||
async function parseFile(tsconfigFile) { | ||
async function parseFile(tsconfigFile, cache) { | ||
if (cache == null ? void 0 : cache.has(tsconfigFile)) { | ||
return cache.get(tsconfigFile); | ||
} | ||
try { | ||
const tsconfigJson = await fs3.readFile(tsconfigFile, "utf-8"); | ||
const json = toJson(tsconfigJson); | ||
return { | ||
const result = { | ||
filename: tsconfigFile, | ||
tsconfig: normalizeTSConfig(JSON.parse(json), path3.dirname(tsconfigFile)) | ||
}; | ||
cache == null ? void 0 : cache.set(tsconfigFile, result); | ||
return result; | ||
} catch (e) { | ||
@@ -347,3 +363,3 @@ throw new ParseError(`parsing ${tsconfigFile} failed: ${e}`, "PARSE_FILE", e); | ||
} | ||
async function parseReferences(result) { | ||
async function parseReferences(result, cache) { | ||
if (!result.tsconfig.references) { | ||
@@ -353,7 +369,7 @@ return; | ||
const referencedFiles = resolveReferencedTSConfigFiles(result); | ||
const referenced = await Promise.all(referencedFiles.map((file) => parseFile(file))); | ||
await Promise.all(referenced.map((ref) => parseExtends(ref))); | ||
const referenced = await Promise.all(referencedFiles.map((file) => parseFile(file, cache))); | ||
await Promise.all(referenced.map((ref) => parseExtends(ref, cache))); | ||
result.referenced = referenced; | ||
} | ||
async function parseExtends(result) { | ||
async function parseExtends(result, cache) { | ||
if (!result.tsconfig.extends) { | ||
@@ -372,3 +388,3 @@ return; | ||
} | ||
extended.push(await parseFile(extendedTSConfigFile)); | ||
extended.push(await parseFile(extendedTSConfigFile, cache)); | ||
} | ||
@@ -476,17 +492,31 @@ result.extended = extended; | ||
import path5 from "path"; | ||
async function parseNative(filename) { | ||
async function parseNative(filename, options) { | ||
const cache = options == null ? void 0 : options.cache; | ||
if (cache == null ? void 0 : cache.has(filename)) { | ||
return cache.get(filename); | ||
} | ||
let tsconfigFile = await resolveTSConfig(filename); | ||
if (tsconfigFile) { | ||
tsconfigFile = native2posix(tsconfigFile); | ||
} else { | ||
if (!tsconfigFile) { | ||
tsconfigFile = await findNative(filename); | ||
} | ||
const ts = await loadTS(); | ||
const result = await parseFile2(tsconfigFile, ts); | ||
await parseReferences2(result, ts); | ||
return resolveSolutionTSConfig(filename, result); | ||
let result; | ||
if (cache == null ? void 0 : cache.has(tsconfigFile)) { | ||
result = cache.get(tsconfigFile); | ||
} else { | ||
const ts = await loadTS(); | ||
result = await parseFile2(tsconfigFile, ts, cache); | ||
await parseReferences2(result, ts, cache); | ||
cache == null ? void 0 : cache.set(tsconfigFile, result); | ||
} | ||
result = resolveSolutionTSConfig(filename, result); | ||
cache == null ? void 0 : cache.set(filename, result); | ||
return result; | ||
} | ||
async function parseFile2(tsconfigFile, ts) { | ||
async function parseFile2(tsconfigFile, ts, cache) { | ||
if (cache == null ? void 0 : cache.has(tsconfigFile)) { | ||
return cache.get(tsconfigFile); | ||
} | ||
const posixTSConfigFile = native2posix(tsconfigFile); | ||
const { parseJsonConfigFileContent, readConfigFile, sys } = ts; | ||
const { config, error } = readConfigFile(tsconfigFile, sys.readFile); | ||
const { config, error } = readConfigFile(posixTSConfigFile, sys.readFile); | ||
if (error) { | ||
@@ -501,12 +531,13 @@ throw new ParseNativeError(error, null); | ||
}; | ||
const nativeResult = parseJsonConfigFileContent(config, host, path5.dirname(tsconfigFile), void 0, tsconfigFile); | ||
const nativeResult = parseJsonConfigFileContent(config, host, path5.dirname(posixTSConfigFile), void 0, posixTSConfigFile); | ||
checkErrors(nativeResult); | ||
const result = { | ||
filename: posix2native(tsconfigFile), | ||
filename: tsconfigFile, | ||
tsconfig: result2tsconfig(nativeResult, ts), | ||
result: nativeResult | ||
}; | ||
cache == null ? void 0 : cache.set(tsconfigFile, result); | ||
return result; | ||
} | ||
async function parseReferences2(result, ts) { | ||
async function parseReferences2(result, ts, cache) { | ||
if (!result.tsconfig.references) { | ||
@@ -516,3 +547,3 @@ return; | ||
const referencedFiles = resolveReferencedTSConfigFiles(result); | ||
result.referenced = await Promise.all(referencedFiles.map((file) => parseFile2(file, ts))); | ||
result.referenced = await Promise.all(referencedFiles.map((file) => parseFile2(file, ts, cache))); | ||
} | ||
@@ -519,0 +550,0 @@ function checkErrors(nativeResult) { |
{ | ||
"name": "tsconfck", | ||
"version": "1.0.0-4", | ||
"version": "1.0.0-5", | ||
"description": "A utility to work with tsconfig.json without typescript", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
# tsconfck | ||
[![npm version](https://img.shields.io/npm/v/tsconfck)](https://www.npmjs.com/package/tsconfck) | ||
[![CI](https://github.com/dominikg/tsconfck/actions/workflows/test.yml/badge.svg)](https://github.com/dominikg/tsconfck/actions/workflows/test.yml) | ||
A utility to find and parse tsconfig files without depending on typescript | ||
@@ -17,2 +20,8 @@ | ||
# Install | ||
```shell | ||
npm install --save-dev tsconfck # or pnpm, yarn | ||
``` | ||
# Usage | ||
@@ -39,3 +48,3 @@ | ||
filename, // full path to found tsconfig | ||
tsconfig, // tsconfig object including merged values from extended configs | ||
tsconfig, // tsconfig object including merged values from extended configs, normalized | ||
result, // output of ts.parseJsonConfigFileContent | ||
@@ -55,18 +64,22 @@ solution, // solution result if tsconfig is part of a solution | ||
You should cache results to avoid reparsing if you process multiple ts files that share few tsconfig files | ||
You can use a map to cache results and avoid reparsing if you process multiple ts files that share few tsconfig files | ||
```js | ||
import { find, parse } from 'tsconfck'; | ||
import { parse } from 'tsconfck'; | ||
// 1. create cache instance | ||
const cache = new Map(); | ||
const cachedParse = async (filename) => { | ||
const tsconfigFile = find(filename); | ||
if (cache.has(tsconfigFile)) { | ||
return cache.get(tsconfigFile); | ||
} | ||
const parseResult = parse(tsconfigFile); | ||
cache.put(tsconfigFile, parseResult); | ||
return parseResult; | ||
}; | ||
// 2. pass cache instance in options | ||
const fooResult = await parse('src/foo.ts', { cache }); | ||
// 3. profit (if they share the same tsconfig.json, it is not parsed again) | ||
const barResult = await parse('src/bar.ts', { cache }); | ||
``` | ||
> You are responsible for clearing the cache if tsconfig files change on disk during its lifetime. | ||
> | ||
> Always clear the whole cache if anything changes as objects in the cache can ref each other | ||
> Returned results are direct cache objects. | ||
> | ||
> If you want to modify them, deep-clone first. | ||
### cli | ||
@@ -73,0 +86,0 @@ |
@@ -5,3 +5,2 @@ import path from 'path'; | ||
native2posix, | ||
posix2native, | ||
resolveReferencedTSConfigFiles, | ||
@@ -19,24 +18,46 @@ resolveSolutionTSConfig, | ||
* @param {string} filename - path to a tsconfig.json or a .ts source file (absolute or relative to cwd) | ||
* @param {ParseNativeOptions} options - options | ||
* @returns {Promise<ParseNativeResult>} | ||
* @throws {ParseNativeError} | ||
*/ | ||
export async function parseNative(filename: string): Promise<ParseNativeResult> { | ||
export async function parseNative( | ||
filename: string, | ||
options?: ParseNativeOptions | ||
): Promise<ParseNativeResult> { | ||
const cache = options?.cache; | ||
if (cache?.has(filename)) { | ||
return cache.get(filename)!; | ||
} | ||
let tsconfigFile = await resolveTSConfig(filename); | ||
if (tsconfigFile) { | ||
// convert to C:/foo/bar on windows as ts.readConfigFile expects it that way | ||
tsconfigFile = native2posix(tsconfigFile); | ||
} else { | ||
if (!tsconfigFile) { | ||
tsconfigFile = await findNative(filename); | ||
} | ||
let result: ParseNativeResult; | ||
if (cache?.has(tsconfigFile)) { | ||
result = cache.get(tsconfigFile)!; | ||
} else { | ||
const ts = await loadTS(); | ||
result = await parseFile(tsconfigFile, ts, cache); | ||
await parseReferences(result, ts, cache); | ||
cache?.set(tsconfigFile, result); | ||
} | ||
const ts = await loadTS(); | ||
const result = await parseFile(tsconfigFile, ts); | ||
await parseReferences(result, ts); | ||
//@ts-ignore | ||
return resolveSolutionTSConfig(filename, result); | ||
result = resolveSolutionTSConfig(filename, result); | ||
//@ts-ignore | ||
cache?.set(filename, result); | ||
return result; | ||
} | ||
async function parseFile(tsconfigFile: string, ts: any) { | ||
async function parseFile( | ||
tsconfigFile: string, | ||
ts: any, | ||
cache?: Map<string, ParseNativeResult> | ||
): Promise<ParseNativeResult> { | ||
if (cache?.has(tsconfigFile)) { | ||
return cache.get(tsconfigFile)!; | ||
} | ||
const posixTSConfigFile = native2posix(tsconfigFile); | ||
const { parseJsonConfigFileContent, readConfigFile, sys } = ts; | ||
const { config, error } = readConfigFile(tsconfigFile, sys.readFile); | ||
const { config, error } = readConfigFile(posixTSConfigFile, sys.readFile); | ||
if (error) { | ||
@@ -56,16 +77,21 @@ throw new ParseNativeError(error, null); | ||
host, | ||
path.dirname(tsconfigFile), | ||
path.dirname(posixTSConfigFile), | ||
undefined, | ||
tsconfigFile | ||
posixTSConfigFile | ||
); | ||
checkErrors(nativeResult); | ||
const result: ParseNativeResult = { | ||
filename: posix2native(tsconfigFile), | ||
filename: tsconfigFile, | ||
tsconfig: result2tsconfig(nativeResult, ts), | ||
result: nativeResult | ||
}; | ||
cache?.set(tsconfigFile, result); | ||
return result; | ||
} | ||
async function parseReferences(result: ParseNativeResult, ts: any) { | ||
async function parseReferences( | ||
result: ParseNativeResult, | ||
ts: any, | ||
cache?: Map<string, ParseNativeResult> | ||
) { | ||
if (!result.tsconfig.references) { | ||
@@ -75,3 +101,3 @@ return; | ||
const referencedFiles = resolveReferencedTSConfigFiles(result); | ||
result.referenced = await Promise.all(referencedFiles.map((file) => parseFile(file, ts))); | ||
result.referenced = await Promise.all(referencedFiles.map((file) => parseFile(file, ts, cache))); | ||
} | ||
@@ -185,2 +211,13 @@ | ||
export interface ParseNativeOptions { | ||
/** | ||
* optional cache map to speed up repeated parsing with multiple files | ||
* it is your own responsibility to clear the cache if tsconfig files change during its lifetime | ||
* cache keys are input `filename` and absolute paths to tsconfig.json files | ||
* | ||
* You must not modify cached values. | ||
*/ | ||
cache?: Map<string, ParseNativeResult>; | ||
} | ||
export interface ParseNativeResult { | ||
@@ -187,0 +224,0 @@ /** |
@@ -18,20 +18,41 @@ import path from 'path'; | ||
* @param {string} filename - path to a tsconfig.json or a .ts source file (absolute or relative to cwd) | ||
* @param {ParseOptions} options - options | ||
* @returns {Promise<ParseResult>} | ||
* @throws {ParseError} | ||
*/ | ||
export async function parse(filename: string): Promise<ParseResult> { | ||
export async function parse(filename: string, options?: ParseOptions): Promise<ParseResult> { | ||
const cache = options?.cache; | ||
if (cache?.has(filename)) { | ||
return cache.get(filename)!; | ||
} | ||
const tsconfigFile = (await resolveTSConfig(filename)) || (await find(filename)); | ||
const result = await parseFile(tsconfigFile); | ||
await Promise.all([parseExtends(result), parseReferences(result)]); | ||
return resolveSolutionTSConfig(filename, result); | ||
let result; | ||
if (cache?.has(tsconfigFile)) { | ||
result = cache.get(tsconfigFile)!; | ||
} else { | ||
result = await parseFile(tsconfigFile, cache); | ||
await Promise.all([parseExtends(result, cache), parseReferences(result, cache)]); | ||
cache?.set(tsconfigFile, result); | ||
} | ||
result = resolveSolutionTSConfig(filename, result); | ||
cache?.set(filename, result); | ||
return result; | ||
} | ||
async function parseFile(tsconfigFile: string): Promise<ParseResult> { | ||
async function parseFile( | ||
tsconfigFile: string, | ||
cache?: Map<string, ParseResult> | ||
): Promise<ParseResult> { | ||
if (cache?.has(tsconfigFile)) { | ||
return cache.get(tsconfigFile)!; | ||
} | ||
try { | ||
const tsconfigJson = await fs.readFile(tsconfigFile, 'utf-8'); | ||
const json = toJson(tsconfigJson); | ||
return { | ||
const result = { | ||
filename: tsconfigFile, | ||
tsconfig: normalizeTSConfig(JSON.parse(json), path.dirname(tsconfigFile)) | ||
}; | ||
cache?.set(tsconfigFile, result); | ||
return result; | ||
} catch (e) { | ||
@@ -55,3 +76,3 @@ throw new ParseError(`parsing ${tsconfigFile} failed: ${e}`, 'PARSE_FILE', e); | ||
async function parseReferences(result: ParseResult) { | ||
async function parseReferences(result: ParseResult, cache?: Map<string, ParseResult>) { | ||
if (!result.tsconfig.references) { | ||
@@ -61,8 +82,8 @@ return; | ||
const referencedFiles = resolveReferencedTSConfigFiles(result); | ||
const referenced = await Promise.all(referencedFiles.map((file) => parseFile(file))); | ||
await Promise.all(referenced.map((ref) => parseExtends(ref))); | ||
const referenced = await Promise.all(referencedFiles.map((file) => parseFile(file, cache))); | ||
await Promise.all(referenced.map((ref) => parseExtends(ref, cache))); | ||
result.referenced = referenced; | ||
} | ||
async function parseExtends(result: ParseResult) { | ||
async function parseExtends(result: ParseResult, cache?: Map<string, ParseResult>) { | ||
if (!result.tsconfig.extends) { | ||
@@ -87,3 +108,3 @@ return; | ||
} | ||
extended.push(await parseFile(extendedTSConfigFile)); | ||
extended.push(await parseFile(extendedTSConfigFile, cache)); | ||
} | ||
@@ -195,2 +216,13 @@ result.extended = extended; | ||
export interface ParseOptions { | ||
/** | ||
* optional cache map to speed up repeated parsing with multiple files | ||
* it is your own responsibility to clear the cache if tsconfig files change during its lifetime | ||
* cache keys are input `filename` and absolute paths to tsconfig.json files | ||
* | ||
* You must not modify cached values. | ||
*/ | ||
cache?: Map<string, ParseResult>; | ||
} | ||
export interface ParseResult { | ||
@@ -240,5 +272,5 @@ /** | ||
/** | ||
* code of typescript diagnostic, prefixed with "TS " | ||
* the cause of this error | ||
*/ | ||
cause: Error | undefined; | ||
} |
@@ -103,4 +103,8 @@ import path from 'path'; | ||
export function resolveSolutionTSConfig(filename: string, result: ParseResult): ParseResult { | ||
if (['.ts', '.tsx'].some((ext) => filename.endsWith(ext)) && !isIncluded(filename, result)) { | ||
const solutionTSConfig = result.referenced?.find((referenced) => | ||
if ( | ||
result.referenced && | ||
['.ts', '.tsx'].some((ext) => filename.endsWith(ext)) && | ||
!isIncluded(filename, result) | ||
) { | ||
const solutionTSConfig = result.referenced.find((referenced) => | ||
isIncluded(filename, referenced) | ||
@@ -107,0 +111,0 @@ ); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
173999
2359
142