Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: align native functions with changes for not found, improve errorhandling #121

Merged
merged 4 commits into from
Sep 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/chatty-garlics-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'tsconfck': patch
---

fix(parseNative): return empty result for not found to align with parse
5 changes: 5 additions & 0 deletions .changeset/shy-lamps-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'tsconfck': major
---

breaking(errors): throw ENOENT from parse if input .json filename does not exist. throw custom error if exists but is no file
2 changes: 1 addition & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export function findNative(filename: string, options?: TSConfckFindOptions | und
*
* You need to have `typescript` installed to use this
*
* @param filename - path to a tsconfig.json or a .ts source file (absolute or relative to cwd)
* @param filename - path to a tsconfig .json or a source file (absolute or relative to cwd)
* @param options - options
* */
export function parseNative(filename: string, options?: TSConfckParseNativeOptions | undefined): Promise<TSConfckParseNativeResult>;
Expand Down
4 changes: 2 additions & 2 deletions packages/tsconfck/src/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class TSConfckCache {
* @internal
* @private
* @param file
* @param {Promise<T>|T} result
* @param {Promise<T>} result
*/
setParseResult(file, result) {
this.#parsed.set(file, result);
Expand All @@ -69,7 +69,7 @@ export class TSConfckCache {
* @internal
* @private
* @param {string} dir
* @param {Promise<string|null>|string|null} tsconfigPath
* @param {Promise<string|null>} tsconfigPath
*/
setTSConfigPath(dir, tsconfigPath) {
this.#tsconfigPaths.set(dir, tsconfigPath);
Expand Down
18 changes: 5 additions & 13 deletions packages/tsconfck/src/find-native.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,17 @@ export async function findNative(filename, options) {
const cache = options?.cache;
const root = options?.root ? path.resolve(options.root) : undefined;
if (cache?.hasTSConfigPath(fileDir)) {
const tsconfigFile = await cache.getTSConfigPath(fileDir);
if (!tsconfigFile) {
throw new Error(`no tsconfig file found for ${filename}`);
}
return tsconfigFile;
return cache.getTSConfigPath(fileDir);
}
const ts = await loadTS();
const { findConfigFile, sys } = ts;
let tsconfigFile = findConfigFile(fileDir, sys.fileExists);
if (is_out_of_root(tsconfigFile, root)) {
if (!tsconfigFile || is_out_of_root(tsconfigFile, root)) {
tsconfigFile = null;
}
if (cache) {
cache_result(tsconfigFile, fileDir, cache, root);
}
if (!tsconfigFile) {
throw new Error(`no tsconfig file found for ${filename}`);
}
return tsconfigFile;
}

Expand All @@ -48,9 +41,9 @@ function is_out_of_root(tsconfigFile, root) {
/**
* add all intermediate directories between fileDir and tsconfigFile to cache
* if no tsconfig was found, go up until root
* @param {string} tsconfigFile
* @param {string|null} tsconfigFile
* @param {string} fileDir
* @param {TSConfckCache} cache
* @param {import('./cache.js').TSConfckCache} cache
* @param {string} [root]
*/
function cache_result(tsconfigFile, fileDir, cache, root) {
Expand All @@ -66,8 +59,7 @@ function cache_result(tsconfigFile, fileDir, cache, root) {
dir = parent;
}
}
const p = Promise.resolve(tsconfigFile);
directories.forEach((d) => {
cache.setTSConfigPath(d, p);
cache.setTSConfigPath(d, Promise.resolve(tsconfigFile));
});
}
67 changes: 32 additions & 35 deletions packages/tsconfck/src/parse-native.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@ import {
native2posix,
resolveReferencedTSConfigFiles,
resolveSolutionTSConfig,
resolveTSConfig
resolveTSConfigJson
} from './util.js';
import { findNative } from './find-native.js';

const notFoundResult = {
tsconfigFile: null,
tsconfig: {},
result: null
};

/**
* parse the closest tsconfig.json file with typescript native functions
*
* You need to have `typescript` installed to use this
*
* @param {string} filename - path to a tsconfig.json or a .ts source file (absolute or relative to cwd)
* @param {string} filename - path to a tsconfig .json or a source file (absolute or relative to cwd)
* @param {import('./public.d.ts').TSConfckParseNativeOptions} [options] - options
* @returns {Promise<import('./public.d.ts').TSConfckParseNativeResult>}
* @throws {TSConfckParseNativeError}
Expand All @@ -24,58 +30,46 @@ export async function parseNative(filename, options) {
if (cache?.hasParseResult(filename)) {
return cache.getParseResult(filename);
}
let tsconfigFile;

if (options?.resolveWithEmptyIfConfigNotFound) {
try {
tsconfigFile = await resolveTSConfig(filename, cache);
if (!tsconfigFile) {
tsconfigFile = await findNative(filename, options);
}
} catch (e) {
const notFoundResult = {
tsconfigFile: 'no_tsconfig_file_found',
tsconfig: {},
result: null
};
cache?.setParseResult(filename, Promise.resolve(notFoundResult));
return notFoundResult;
}
} else {
tsconfigFile = await resolveTSConfig(filename, cache);
if (!tsconfigFile) {
tsconfigFile = await findNative(filename, options);
}
/** @type {(result: import('./public.d.ts').TSConfckParseNativeResult)=>void}*/
let resolveConfigPromise;
/** @type {Promise<import('./public.d.ts').TSConfckParseNativeResult>}*/
const configPromise = new Promise((r) => {
resolveConfigPromise = r;
});
cache?.setParseResult(filename, configPromise);
const tsconfigFile =
(await resolveTSConfigJson(filename, cache)) || (await findNative(filename, options));
if (!tsconfigFile) {
resolveConfigPromise(notFoundResult);
return configPromise;
}

/** @type {import('./public.d.ts').TSConfckParseNativeResult} */
let result;
if (cache?.hasParseResult(tsconfigFile)) {
if (filename !== tsconfigFile && cache?.hasParseResult(tsconfigFile)) {
result = await cache.getParseResult(tsconfigFile);
} else {
const ts = await loadTS();
result = await parseFile(tsconfigFile, ts, options);
result = await parseFile(tsconfigFile, ts, options, filename === tsconfigFile);
await parseReferences(result, ts, options);
cache?.setParseResult(tsconfigFile, Promise.resolve(result));
}

//@ts-ignore
result = resolveSolutionTSConfig(filename, result);
//@ts-ignore
cache?.setParseResult(filename, Promise.resolve(result));
return result;
resolveConfigPromise(resolveSolutionTSConfig(filename, result));
return configPromise;
}

/**
*
* @param {string} tsconfigFile
* @param {any} ts
* @param {import('./public.d.ts').TSConfckParseNativeOptions} [options]
* @returns {Promise<import('./public.d.ts').TSConfckParseNativeResult>}
* @param {boolean} [skipCache]
* @returns {import('./public.d.ts').TSConfckParseNativeResult}
*/
async function parseFile(tsconfigFile, ts, options) {
function parseFile(tsconfigFile, ts, options, skipCache) {
const cache = options?.cache;
if (cache?.hasParseResult(tsconfigFile)) {
if (!skipCache && cache?.hasParseResult(tsconfigFile)) {
return cache.getParseResult(tsconfigFile);
}
const posixTSConfigFile = native2posix(tsconfigFile);
Expand Down Expand Up @@ -111,7 +105,9 @@ async function parseFile(tsconfigFile, ts, options) {
tsconfig: result2tsconfig(nativeResult, ts),
result: nativeResult
};
cache?.setParseResult(tsconfigFile, Promise.resolve(result));
if (!skipCache) {
cache?.setParseResult(tsconfigFile, Promise.resolve(result));
}
return result;
}

Expand All @@ -129,6 +125,7 @@ async function parseReferences(result, ts, options) {
result.referenced = await Promise.all(
referencedFiles.map((file) => parseFile(file, ts, options))
);
result.referenced.forEach((ref) => (ref.solution = result));
}

/**
Expand Down
10 changes: 7 additions & 3 deletions packages/tsconfck/src/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
resolve2posix,
resolveReferencedTSConfigFiles,
resolveSolutionTSConfig,
resolveTSConfig
resolveTSConfigJson
} from './util.js';

const not_found_result = {
Expand Down Expand Up @@ -38,7 +38,8 @@ export async function parse(filename, options) {
});
cache?.setParseResult(filename, configPromise);

let tsconfigFile = (await resolveTSConfig(filename, cache)) || (await find(filename, options));
let tsconfigFile =
(await resolveTSConfigJson(filename, cache)) || (await find(filename, options));
if (!tsconfigFile) {
resolveConfigPromise(not_found_result);
return configPromise;
Expand Down Expand Up @@ -106,7 +107,7 @@ function normalizeTSConfig(tsconfig, dir) {
/**
*
* @param {import('./public.d.ts').TSConfckParseResult} result
* @param {TSConfckCache} [cache]
* @param {import('./cache.js').TSConfckCache} [cache]
* @returns {Promise<void>}
*/
async function parseReferences(result, cache) {
Expand All @@ -116,6 +117,9 @@ async function parseReferences(result, cache) {
const referencedFiles = resolveReferencedTSConfigFiles(result);
const referenced = await Promise.all(referencedFiles.map((file) => parseFile(file, cache)));
await Promise.all(referenced.map((ref) => parseExtends(ref, cache)));
referenced.forEach((ref) => {
ref.solution = result;
});
result.referenced = referenced;
}

Expand Down
70 changes: 22 additions & 48 deletions packages/tsconfck/src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,36 +31,23 @@ export async function loadTS() {
* @param {import('./cache.js').TSConfckCache} [cache]
* @returns {Promise<string|void>}
*/
export async function resolveTSConfig(filename, cache) {
export async function resolveTSConfigJson(filename, cache) {
if (path.extname(filename) !== '.json') {
return;
return; // ignore files that are not json
}
const tsconfig = path.resolve(filename);
if (cache) {
if (cache.hasParseResult(tsconfig)) {
if (cache.hasParseResult(tsconfig) || cache.hasParseResult(filename)) {
return tsconfig;
}
if (path.basename(tsconfig) === 'tsconfig.json') {
const dir = path.dirname(tsconfig);
if (cache.hasTSConfigPath(dir)) {
const cached = await cache.getTSConfigPath(dir);
return cached === tsconfig ? tsconfig : undefined;
}
}
}

try {
const stat = await fs.stat(tsconfig);
return fs.stat(tsconfig).then((stat) => {
if (stat.isFile() || stat.isFIFO()) {
return tsconfig;
} else {
throw new Error(`${filename} exists but is not a regular file.`);
}
} catch (e) {
// ignore does not exist error
if (e.code !== 'ENOENT') {
throw e;
}
}
throw new Error(`no tsconfig file found for ${filename}`);
});
}

/**
Expand All @@ -70,16 +57,12 @@ export async function resolveTSConfig(filename, cache) {
* windows: C:/foo/bar -> c:\foo\bar
* linux: /foo/bar -> /foo/bar
*
* @param filename {string} filename with posix separators
* @param {string} filename with posix separators
* @returns {string} filename with native separators
*/
export const posix2native = IS_POSIX
? (s) => s
: (filename) => {
return filename.includes(path.posix.sep)
? filename.replace(POSIX_SEP_RE, path.sep)
: filename;
};
? (filename) => filename
: (filename) => filename.replace(POSIX_SEP_RE, path.sep);

/**
* convert native separator to posix separator
Expand All @@ -88,16 +71,12 @@ export const posix2native = IS_POSIX
* windows: C:\foo\bar -> c:/foo/bar
* linux: /foo/bar -> /foo/bar
*
* @param filename {string} filename with native separators
* @param {string} filename - filename with native separators
* @returns {string} filename with posix separators
*/
export const native2posix = IS_POSIX
? (s) => s
: (filename) => {
return filename.includes(path.sep)
? filename.replace(NATIVE_SEP_RE, path.posix.sep)
: filename;
};
? (filename) => filename
: (filename) => filename.replace(NATIVE_SEP_RE, path.posix.sep);

/**
* converts params to native separator, resolves path and converts native back to posix
Expand All @@ -108,16 +87,14 @@ export const native2posix = IS_POSIX
* @param filename {string} filename or pattern to resolve
* @returns string
*/
export function resolve2posix(dir, filename) {
if (IS_POSIX) {
return dir ? path.resolve(dir, filename) : path.resolve(filename);
}
return native2posix(
dir
? path.resolve(posix2native(dir), posix2native(filename))
: path.resolve(posix2native(filename))
);
}
export const resolve2posix = IS_POSIX
? (dir, filename) => (dir ? path.resolve(dir, filename) : path.resolve(filename))
: (dir, filename) =>
native2posix(
dir
? path.resolve(posix2native(dir), posix2native(filename))
: path.resolve(posix2native(filename))
);

/**
*
Expand Down Expand Up @@ -147,10 +124,7 @@ export function resolveSolutionTSConfig(filename, result) {
isIncluded(filename, referenced)
);
if (solutionTSConfig) {
return {
...solutionTSConfig,
solution: result
};
return solutionTSConfig;
}
}
return result;
Expand Down
Loading