From f1b4ea2d9619fa63462ee0f11e7907e56c462e7c Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 3 Jun 2022 16:29:31 -0400 Subject: [PATCH] fix: included symlinked directories when reading a directory (#1290) --- deno/common/DenoRuntime.ts | 43 +++++++----------- deno/common/ts_morph_common.d.ts | 17 +++---- deno/common/ts_morph_common.js | 44 ++++++++++++++++--- deno/ts_morph.d.ts | 4 ++ deno/ts_morph.js | 6 +++ packages/common/lib/ts-morph-common.d.ts | 17 +++---- .../src/fileSystem/RealFileSystemHost.ts | 40 ++++++++++++++--- .../common/src/runtimes/BrowserRuntime.ts | 14 ++---- packages/common/src/runtimes/DenoRuntime.ts | 43 +++++++----------- packages/common/src/runtimes/NodeRuntime.ts | 39 ++++------------ packages/common/src/runtimes/Runtime.ts | 17 +++---- packages/ts-morph/lib/ts-morph.d.ts | 4 ++ packages/ts-morph/src/compiler/types/Type.ts | 12 ++--- 13 files changed, 158 insertions(+), 142 deletions(-) diff --git a/deno/common/DenoRuntime.ts b/deno/common/DenoRuntime.ts index 07255f45c..ce0df1e2b 100644 --- a/deno/common/DenoRuntime.ts +++ b/deno/common/DenoRuntime.ts @@ -90,38 +90,25 @@ class DenoRuntimeFileSystem { return Deno.copyFileSync(srcPath, destPath); } - async fileExists(filePath: string) { - try { - const stat = await Deno.stat(filePath); - return stat.isFile; - } catch { - return false; - } - } - - fileExistsSync(filePath: string) { - try { - return Deno.statSync(filePath).isFile; - } catch { - return false; - } + async stat(filePath: string) { + const stat = await Deno.stat(filePath); + return this._toStat(stat); } - async directoryExists(dirPath: string) { - try { - const stat = await Deno.stat(dirPath); - return stat.isDirectory; - } catch { - return false; - } + statSync(path: string) { + const stat = Deno.statSync(path); + return this._toStat(stat); } - directoryExistsSync(dirPath: string) { - try { - return Deno.statSync(dirPath).isDirectory; - } catch (err) { - return false; - } + private _toStat(stat: Deno.FileInfo) { + return { + isFile() { + return stat.isFile; + }, + isDirectory() { + return stat.isDirectory; + }, + }; } realpathSync(path: string) { diff --git a/deno/common/ts_morph_common.d.ts b/deno/common/ts_morph_common.d.ts index 6342f663c..8866d8b39 100644 --- a/deno/common/ts_morph_common.d.ts +++ b/deno/common/ts_morph_common.d.ts @@ -914,6 +914,11 @@ export interface RuntimeDirEntry { isSymlink: boolean; } +export interface RuntimeFileInfo { + isFile(): boolean; + isDirectory(): boolean; +} + export interface RuntimeFileSystem { /** Gets if this file system is case sensitive. */ isCaseSensitive(): boolean; @@ -943,14 +948,10 @@ export interface RuntimeFileSystem { copy(srcPath: string, destPath: string): Promise; /** Synchronously copies a file or directory. */ copySync(srcPath: string, destPath: string): void; - /** Asynchronously checks if a file exists. */ - fileExists(filePath: string): Promise; - /** Synchronously checks if a file exists. */ - fileExistsSync(filePath: string): boolean; - /** Asynchronously checks if a directory exists. */ - directoryExists(dirPath: string): Promise; - /** Synchronously checks if a directory exists. */ - directoryExistsSync(dirPath: string): boolean; + /** Asynchronously gets the path's stat information. */ + stat(path: string): Promise; + /** Synchronously gets the path's stat information. */ + statSync(path: string): RuntimeFileInfo; /** See https://nodejs.org/api/fs.html#fs_fs_realpathsync_path_options */ realpathSync(path: string): string; /** Gets the current directory of the environment. */ diff --git a/deno/common/ts_morph_common.js b/deno/common/ts_morph_common.js index 1770c41f5..363c0f914 100644 --- a/deno/common/ts_morph_common.js +++ b/deno/common/ts_morph_common.js @@ -1605,8 +1605,18 @@ class RealFileSystemHost { readDirSync(dirPath) { try { const entries = fs.readDirSync(dirPath); - for (const entry of entries) + for (const entry of entries) { entry.name = FileUtils.pathJoin(dirPath, entry.name); + if (entry.isSymlink) { + try { + const info = fs.statSync(entry.name); + entry.isDirectory = info.isDirectory(); + entry.isFile = info.isFile(); + } + catch (_a) { + } + } + } return entries; } catch (err) { @@ -1653,17 +1663,37 @@ class RealFileSystemHost { copySync(srcPath, destPath) { fs.copySync(srcPath, destPath); } - fileExists(filePath) { - return fs.fileExists(filePath); + async fileExists(filePath) { + try { + return (await fs.stat(filePath)).isFile(); + } + catch (_a) { + return false; + } } fileExistsSync(filePath) { - return fs.fileExistsSync(filePath); + try { + return fs.statSync(filePath).isFile(); + } + catch (_a) { + return false; + } } - directoryExists(dirPath) { - return fs.directoryExists(dirPath); + async directoryExists(dirPath) { + try { + return (await fs.stat(dirPath)).isDirectory(); + } + catch (_a) { + return false; + } } directoryExistsSync(dirPath) { - return fs.directoryExistsSync(dirPath); + try { + return fs.statSync(dirPath).isDirectory(); + } + catch (_a) { + return false; + } } realpathSync(path) { return fs.realpathSync(path); diff --git a/deno/ts_morph.d.ts b/deno/ts_morph.d.ts index f0b5e4c99..6c8150302 100644 --- a/deno/ts_morph.d.ts +++ b/deno/ts_morph.d.ts @@ -9214,6 +9214,8 @@ export declare class Symbol { getExportSymbol(): Symbol; /** Gets if the symbol is an alias. */ isAlias(): boolean; + /** Gets if the symbol is optional. */ + isOptional(): boolean; /** Gets the symbol flags. */ getFlags(): SymbolFlags; /** @@ -10124,6 +10126,8 @@ export declare class Type { isAny(): boolean; /** Gets if this is an array type. */ isArray(): boolean; + /** Gets if this is a template literal type. */ + isTemplateLiteral(): boolean; /** Gets if this is a boolean type. */ isBoolean(): boolean; /** Gets if this is a string type. */ diff --git a/deno/ts_morph.js b/deno/ts_morph.js index 148ea0d1e..df59ade78 100644 --- a/deno/ts_morph.js +++ b/deno/ts_morph.js @@ -16840,6 +16840,9 @@ class Symbol { isAlias() { return (this.getFlags() & SymbolFlags.Alias) === SymbolFlags.Alias; } + isOptional() { + return (this.getFlags() & SymbolFlags.Optional) === SymbolFlags.Optional; + } getFlags() { return this.compilerSymbol.getFlags(); } @@ -17971,6 +17974,9 @@ class Type { return false; return symbol.getName() === "Array" && this.getTypeArguments().length === 1; } + isTemplateLiteral() { + return this._hasTypeFlag(TypeFlags.TemplateLiteral); + } isBoolean() { return this._hasTypeFlag(TypeFlags.Boolean); } diff --git a/packages/common/lib/ts-morph-common.d.ts b/packages/common/lib/ts-morph-common.d.ts index 330b97d55..bb46f0fd2 100644 --- a/packages/common/lib/ts-morph-common.d.ts +++ b/packages/common/lib/ts-morph-common.d.ts @@ -913,6 +913,11 @@ export interface RuntimeDirEntry { isSymlink: boolean; } +export interface RuntimeFileInfo { + isFile(): boolean; + isDirectory(): boolean; +} + export interface RuntimeFileSystem { /** Gets if this file system is case sensitive. */ isCaseSensitive(): boolean; @@ -942,14 +947,10 @@ export interface RuntimeFileSystem { copy(srcPath: string, destPath: string): Promise; /** Synchronously copies a file or directory. */ copySync(srcPath: string, destPath: string): void; - /** Asynchronously checks if a file exists. */ - fileExists(filePath: string): Promise; - /** Synchronously checks if a file exists. */ - fileExistsSync(filePath: string): boolean; - /** Asynchronously checks if a directory exists. */ - directoryExists(dirPath: string): Promise; - /** Synchronously checks if a directory exists. */ - directoryExistsSync(dirPath: string): boolean; + /** Asynchronously gets the path's stat information. */ + stat(path: string): Promise; + /** Synchronously gets the path's stat information. */ + statSync(path: string): RuntimeFileInfo; /** See https://nodejs.org/api/fs.html#fs_fs_realpathsync_path_options */ realpathSync(path: string): string; /** Gets the current directory of the environment. */ diff --git a/packages/common/src/fileSystem/RealFileSystemHost.ts b/packages/common/src/fileSystem/RealFileSystemHost.ts index 62e005442..37009cdbc 100644 --- a/packages/common/src/fileSystem/RealFileSystemHost.ts +++ b/packages/common/src/fileSystem/RealFileSystemHost.ts @@ -29,8 +29,18 @@ export class RealFileSystemHost implements FileSystemHost { readDirSync(dirPath: string): RuntimeDirEntry[] { try { const entries = fs.readDirSync(dirPath); - for (const entry of entries) + for (const entry of entries) { entry.name = FileUtils.pathJoin(dirPath, entry.name); + if (entry.isSymlink) { + try { + const info = fs.statSync(entry.name); + entry.isDirectory = info.isDirectory(); + entry.isFile = info.isFile(); + } catch { + // ignore + } + } + } return entries; } catch (err) { throw this.getDirectoryNotFoundErrorIfNecessary(err, dirPath); @@ -96,23 +106,39 @@ export class RealFileSystemHost implements FileSystemHost { } /** @inheritdoc */ - fileExists(filePath: string) { - return fs.fileExists(filePath); + async fileExists(filePath: string) { + try { + return (await fs.stat(filePath)).isFile(); + } catch { + return false; + } } /** @inheritdoc */ fileExistsSync(filePath: string) { - return fs.fileExistsSync(filePath); + try { + return fs.statSync(filePath).isFile(); + } catch { + return false; + } } /** @inheritdoc */ - directoryExists(dirPath: string) { - return fs.directoryExists(dirPath); + async directoryExists(dirPath: string) { + try { + return (await fs.stat(dirPath)).isDirectory(); + } catch { + return false; + } } /** @inheritdoc */ directoryExistsSync(dirPath: string) { - return fs.directoryExistsSync(dirPath); + try { + return fs.statSync(dirPath).isDirectory(); + } catch { + return false; + } } /** @inheritdoc */ diff --git a/packages/common/src/runtimes/BrowserRuntime.ts b/packages/common/src/runtimes/BrowserRuntime.ts index 07fd33190..dc82f7541 100644 --- a/packages/common/src/runtimes/BrowserRuntime.ts +++ b/packages/common/src/runtimes/BrowserRuntime.ts @@ -1,5 +1,5 @@ import minimatch from "minimatch"; -import { Runtime, RuntimeDirEntry, RuntimeFileSystem, RuntimePath } from "./Runtime"; +import { Runtime, RuntimeDirEntry, RuntimeFileInfo, RuntimeFileSystem, RuntimePath } from "./Runtime"; const path = require("path-browserify"); @@ -90,19 +90,11 @@ class BrowserRuntimeFileSystem implements RuntimeFileSystem { throw new Error(this._errorMessage); } - fileExists(_filePath: string): Promise { + stat(_path: string): Promise { return Promise.reject(new Error(this._errorMessage)); } - fileExistsSync(_filePath: string): boolean { - throw new Error(this._errorMessage); - } - - directoryExists(_dirPath: string): Promise { - return Promise.reject(new Error(this._errorMessage)); - } - - directoryExistsSync(_dirPath: string): boolean { + statSync(_path: string): RuntimeFileInfo { throw new Error(this._errorMessage); } diff --git a/packages/common/src/runtimes/DenoRuntime.ts b/packages/common/src/runtimes/DenoRuntime.ts index 31dc98ef6..dc2871b77 100644 --- a/packages/common/src/runtimes/DenoRuntime.ts +++ b/packages/common/src/runtimes/DenoRuntime.ts @@ -95,38 +95,25 @@ class DenoRuntimeFileSystem { return Deno.copyFileSync(srcPath, destPath); } - async fileExists(filePath: string) { - try { - const stat = await Deno.stat(filePath); - return stat.isFile; - } catch { - return false; - } - } - - fileExistsSync(filePath: string) { - try { - return Deno.statSync(filePath).isFile; - } catch { - return false; - } + async stat(filePath: string) { + const stat = await Deno.stat(filePath); + return this._toStat(stat); } - async directoryExists(dirPath: string) { - try { - const stat = await Deno.stat(dirPath); - return stat.isDirectory; - } catch { - return false; - } + statSync(path: string) { + const stat = Deno.statSync(path); + return this._toStat(stat); } - directoryExistsSync(dirPath: string) { - try { - return Deno.statSync(dirPath).isDirectory; - } catch (err) { - return false; - } + private _toStat(stat: Deno.FileInfo) { + return { + isFile() { + return stat.isFile; + }, + isDirectory() { + return stat.isDirectory; + }, + }; } realpathSync(path: string) { diff --git a/packages/common/src/runtimes/NodeRuntime.ts b/packages/common/src/runtimes/NodeRuntime.ts index 5fd5e440c..be0787c46 100644 --- a/packages/common/src/runtimes/NodeRuntime.ts +++ b/packages/common/src/runtimes/NodeRuntime.ts @@ -4,7 +4,7 @@ import minimatch from "minimatch"; import mkdirp from "mkdirp"; import * as os from "os"; import * as path from "path"; -import { Runtime, RuntimeFileSystem, RuntimePath } from "./Runtime"; +import { Runtime, RuntimeFileInfo, RuntimeFileSystem, RuntimePath } from "./Runtime"; export class NodeRuntime implements Runtime { fs = new NodeRuntimeFileSystem(); @@ -135,42 +135,19 @@ class NodeRuntimeFileSystem implements RuntimeFileSystem { fs.copyFileSync(srcPath, destPath); } - fileExists(filePath: string) { - return new Promise(resolve => { - fs.stat(filePath, (err, stat) => { + stat(path: string) { + return new Promise(resolve => { + fs.stat(path, (err, stat) => { if (err) - resolve(false); + throw err; else - resolve(stat.isFile()); + resolve(stat); }); }); } - fileExistsSync(filePath: string) { - try { - return fs.statSync(filePath).isFile(); - } catch (err) { - return false; - } - } - - directoryExists(dirPath: string) { - return new Promise(resolve => { - fs.stat(dirPath, (err, stat) => { - if (err) - resolve(false); - else - resolve(stat.isDirectory()); - }); - }); - } - - directoryExistsSync(dirPath: string) { - try { - return fs.statSync(dirPath).isDirectory(); - } catch (err) { - return false; - } + statSync(path: string) { + return fs.statSync(path); } realpathSync(path: string) { diff --git a/packages/common/src/runtimes/Runtime.ts b/packages/common/src/runtimes/Runtime.ts index e6e005316..a20b482e1 100644 --- a/packages/common/src/runtimes/Runtime.ts +++ b/packages/common/src/runtimes/Runtime.ts @@ -14,6 +14,11 @@ export interface RuntimeDirEntry { isSymlink: boolean; } +export interface RuntimeFileInfo { + isFile(): boolean; + isDirectory(): boolean; +} + export interface RuntimeFileSystem { /** Gets if this file system is case sensitive. */ isCaseSensitive(): boolean; @@ -43,14 +48,10 @@ export interface RuntimeFileSystem { copy(srcPath: string, destPath: string): Promise; /** Synchronously copies a file or directory. */ copySync(srcPath: string, destPath: string): void; - /** Asynchronously checks if a file exists. */ - fileExists(filePath: string): Promise; - /** Synchronously checks if a file exists. */ - fileExistsSync(filePath: string): boolean; - /** Asynchronously checks if a directory exists. */ - directoryExists(dirPath: string): Promise; - /** Synchronously checks if a directory exists. */ - directoryExistsSync(dirPath: string): boolean; + /** Asynchronously gets the path's stat information. */ + stat(path: string): Promise; + /** Synchronously gets the path's stat information. */ + statSync(path: string): RuntimeFileInfo; /** See https://nodejs.org/api/fs.html#fs_fs_realpathsync_path_options */ realpathSync(path: string): string; /** Gets the current directory of the environment. */ diff --git a/packages/ts-morph/lib/ts-morph.d.ts b/packages/ts-morph/lib/ts-morph.d.ts index fad84a500..c261930d8 100644 --- a/packages/ts-morph/lib/ts-morph.d.ts +++ b/packages/ts-morph/lib/ts-morph.d.ts @@ -9214,6 +9214,8 @@ export declare class Symbol { getExportSymbol(): Symbol; /** Gets if the symbol is an alias. */ isAlias(): boolean; + /** Gets if the symbol is optional. */ + isOptional(): boolean; /** Gets the symbol flags. */ getFlags(): SymbolFlags; /** @@ -10124,6 +10126,8 @@ export declare class Type { isAny(): boolean; /** Gets if this is an array type. */ isArray(): boolean; + /** Gets if this is a template literal type. */ + isTemplateLiteral(): boolean; /** Gets if this is a boolean type. */ isBoolean(): boolean; /** Gets if this is a string type. */ diff --git a/packages/ts-morph/src/compiler/types/Type.ts b/packages/ts-morph/src/compiler/types/Type.ts index 18a927a88..b8160cf3e 100644 --- a/packages/ts-morph/src/compiler/types/Type.ts +++ b/packages/ts-morph/src/compiler/types/Type.ts @@ -389,12 +389,12 @@ export class Type { return symbol.getName() === "Array" && this.getTypeArguments().length === 1; } - /** - * Gets if this is a boolean type. - */ - isTemplateLiteral() { - return this._hasTypeFlag(TypeFlags.TemplateLiteral); - } + /** + * Gets if this is a template literal type. + */ + isTemplateLiteral() { + return this._hasTypeFlag(TypeFlags.TemplateLiteral); + } /** * Gets if this is a boolean type.