Skip to content

Commit

Permalink
chore: Add Deno runtime implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed Mar 6, 2021
1 parent bdae94e commit 6608c25
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 8 deletions.
4 changes: 2 additions & 2 deletions packages/bootstrap/src/Project.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CompilerOptionsContainer, createHosts, createModuleResolutionHost, errors, FileSystemHost, FileUtils, InMemoryFileSystemHost, Memoize,
RealFileSystemHost, ResolutionHostFactory, StandardizedFilePath, TransactionalFileSystem, ts, TsConfigResolver } from "@ts-morph/common";
RealFileSystemHost, ResolutionHostFactory, runtime, StandardizedFilePath, TransactionalFileSystem, ts, TsConfigResolver } from "@ts-morph/common";
import { SourceFileCache } from "./SourceFileCache";

/** Options for creating a project. */
Expand Down Expand Up @@ -557,7 +557,7 @@ export class Project {
return ts.formatDiagnosticsWithColorAndContext(diagnostics, {
getCurrentDirectory: () => this._fileSystemWrapper.getCurrentDirectory(),
getCanonicalFileName: fileName => fileName,
getNewLine: () => opts.newLineChar || require("os").EOL,
getNewLine: () => opts.newLineChar || runtime.getEndOfLine(),
});
}

Expand Down
71 changes: 70 additions & 1 deletion packages/common/lib/ts-morph-common.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ export declare class TransactionalFileSystem {
private removeMkDirOperationsForDir;
}

/** Loads the lib files that are stored in a separate module. */
/** Gets the lib files. */
export declare function getLibFiles(): {
fileName: string;
text: string;
Expand Down Expand Up @@ -875,6 +875,75 @@ export declare abstract class SettingsContainer<T extends object> {
onModified(action: () => void): void;
}

export declare const runtime: Runtime;

export interface Runtime {
fs: RuntimeFileSystem;
path: RuntimePath;
getEnvVar(name: string): string | undefined;
getEndOfLine(): string;
getPathMatchesPattern(path: string, pattern: string): boolean;
}

export interface RuntimeFileSystem {
/** Gets if this file system is case sensitive. */
isCaseSensitive(): boolean;
/** Asynchronously deletes the specified file or directory. */
delete(path: string): Promise<void>;
/** Synchronously deletes the specified file or directory */
deleteSync(path: string): void;
/**
* Reads all the child directories and files.
* @remarks Should return just the directory and file names.
*/
readDirSync(dirPath: string): string[];
/** Asynchronously reads a file at the specified path. */
readFile(filePath: string, encoding?: string): Promise<string>;
/** Synchronously reads a file at the specified path. */
readFileSync(filePath: string, encoding?: string): string;
/** Asynchronously writes a file to the file system. */
writeFile(filePath: string, fileText: string): Promise<void>;
/** Synchronously writes a file to the file system. */
writeFileSync(filePath: string, fileText: string): void;
/** Asynchronously creates a directory at the specified path. */
mkdir(dirPath: string): Promise<void>;
/** Synchronously creates a directory at the specified path. */
mkdirSync(dirPath: string): void;
/** Asynchronously moves a file or directory. */
move(srcPath: string, destPath: string): Promise<void>;
/** Synchronously moves a file or directory. */
moveSync(srcPath: string, destPath: string): void;
/** Asynchronously copies a file or directory. */
copy(srcPath: string, destPath: string): Promise<void>;
/** Synchronously copies a file or directory. */
copySync(srcPath: string, destPath: string): void;
/** Asynchronously checks if a file exists. */
fileExists(filePath: string): Promise<boolean>;
/** Synchronously checks if a file exists. */
fileExistsSync(filePath: string): boolean;
/** Asynchronously checks if a directory exists. */
directoryExists(dirPath: string): Promise<boolean>;
/** Synchronously checks if a directory exists. */
directoryExistsSync(dirPath: string): boolean;
/** See https://nodejs.org/api/fs.html#fs_fs_realpathsync_path_options */
realpathSync(path: string): string;
/** Gets the current directory of the environment. */
getCurrentDirectory(): string;
/** Uses pattern matching to find files or directories. */
glob(patterns: ReadonlyArray<string>): Promise<string[]>;
/** Synchronously uses pattern matching to find files or directories. */
globSync(patterns: ReadonlyArray<string>): string[];
}

export interface RuntimePath {
/** Joins the paths. */
join(...paths: string[]): string;
/** Normalizes the provided path. */
normalize(path: string): string;
/** Returns the relative path from `from` to `to`. */
relative(from: string, to: string): string;
}

export declare function matchFiles(this: any, path: string, extensions: ReadonlyArray<string>, excludes: ReadonlyArray<string>, includes: ReadonlyArray<string>, useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getEntries: (path: string) => FileSystemEntries, realpath: (path: string) => string): string[];

export declare function getFileMatcherPatterns(this: any, path: string, excludes: ReadonlyArray<string>, includes: ReadonlyArray<string>, useCaseSensitiveFileNames: boolean, currentDirectory: string): FileMatcherPatterns;
Expand Down
10 changes: 9 additions & 1 deletion packages/common/scripts/buildDeclarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@ for (const [name, declarations] of emitMainFile.getExportedDeclarations()) {
if (writer.getLength() > 0)
writer.newLine();

writer.writeLine(declaration.getText(true));
if (tsMorph.Node.isVariableDeclaration(declaration)) {
const statement = declaration.getVariableStatementOrThrow();
if (statement.getDeclarations().length !== 1)
throw new Error("Only var decls in a statement with a single decl are supported.");
writer.writeLine(statement.getText(true));
}
else {
writer.writeLine(declaration.getText(true));
}
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions packages/common/src/getLibFiles.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as libFiles from "./data/libFiles";
import { StandardizedFilePath } from "./fileSystem";

/** Loads the lib files that are stored in a separate module. */
/** Gets the lib files. */
export function getLibFiles() {
const libFiles: typeof import("./data/libFiles") = require("./data/libFiles");
// these were previously stored in a different module
return libFiles.libFiles;
}

Expand Down
1 change: 1 addition & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from "./fileSystem";
export * from "./getLibFiles";
export * from "./helpers";
export * from "./options";
export * from "./runtimes";
export { CompilerOptionsFromTsConfigOptions, CompilerOptionsFromTsConfigResult, getCompilerOptionsFromTsConfig, TsConfigResolver } from "./tsconfig";
export * from "./typescript";
export * from "./utils";
187 changes: 187 additions & 0 deletions packages/common/src/runtimes/DenoRuntime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// @ts-ignore
import * as stdFs from "https://deno.land/[email protected]/fs/mod.ts";
// @ts-ignore
import * as stdPath from "https://deno.land/[email protected]/path/mod.ts";
// @ts-ignore
import { Runtime, RuntimeFileSystem, RuntimePath } from "./Runtime.ts";

declare var Deno: any;

export class DenoRuntime implements Runtime {
fs = new DenoRuntimeFileSystem();
path = new DenoRuntimePath();

getEnvVar(name: string) {
return Deno.env.get(name);
}

getEndOfLine() {
return Deno.build.os === "win" ? "\r\n" : "\n";
}

getPathMatchesPattern(path: string, pattern: string) {
return stdPath.globToRegExp(pattern, {
extended: true,
globstar: true,
os: "linux", // use the same behaviour across all operating systems
}).test(path);
}
}

class DenoRuntimePath implements RuntimePath {
join(...paths: string[]) {
return stdPath.join(...paths);
}

normalize(path: string) {
return stdPath.normalize(path);
}

relative(from: string, to: string) {
return stdPath.relative(from, to);
}
}

class DenoRuntimeFileSystem implements RuntimeFileSystem {
delete(path: string) {
return Deno.remove(path);
}

deleteSync(path: string) {
Deno.removeSync(path);
}

readDirSync(dirPath: string) {
// @ts-ignore
return Deno.readDirSync(dirPath).map(entry => entry.name);
}

readFile(filePath: string, _encoding = "utf-8") {
return Deno.readTextFile(filePath);
}

readFileSync(filePath: string, _encoding = "utf-8") {
return Deno.readTextFileSync(filePath);
}

writeFile(filePath: string, fileText: string) {
return Deno.writeTextFile(filePath, fileText);
}

writeFileSync(filePath: string, fileText: string) {
return Deno.writeTextFileSync(filePath, fileText);
}

async mkdir(dirPath: string) {
await stdFs.ensureDir(dirPath);
}

mkdirSync(dirPath: string) {
stdFs.ensureDirSync(dirPath);
}

move(srcPath: string, destPath: string) {
return Deno.rename(srcPath, destPath);
}

moveSync(srcPath: string, destPath: string) {
Deno.renameSync(srcPath, destPath);
}

copy(srcPath: string, destPath: string) {
return Deno.copyFile(srcPath, destPath);
}

copySync(srcPath: string, destPath: string) {
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 directoryExists(dirPath: string) {
try {
const stat = await Deno.stat(dirPath);
return stat.isDirectory;
} catch {
return false;
}
}

directoryExistsSync(dirPath: string) {
try {
return Deno.statSync(dirPath).isDirectory;
} catch (err) {
return false;
}
}

realpathSync(path: string) {
return Deno.realPathSync(path);
}

getCurrentDirectory(): string {
return Deno.cwd();
}

glob(patterns: ReadonlyArray<string>) {
const { excludePatterns, pattern } = globPatternsToPattern(patterns);
return stdFs.expandGlob(pattern, {
cwd: this.getCurrentDirectory(),
extended: true,
globstar: true,
exclude: excludePatterns,
});
}

globSync(patterns: ReadonlyArray<string>) {
const { excludePatterns, pattern } = globPatternsToPattern(patterns);
return stdFs.expandGlobSync(pattern, {
root: this.getCurrentDirectory(),
extended: true,
globstar: true,
exclude: excludePatterns,
});
}

isCaseSensitive() {
const platform = Deno.build.os;
return platform !== "win" && platform !== "darwin";
}
}

function globPatternsToPattern(patterns: ReadonlyArray<string>) {
const excludePatterns = [];
const includePatterns = [];

for (const pattern of patterns) {
if (isNegatedGlob(pattern))
excludePatterns.push(pattern);
else
includePatterns.push(pattern);
}

return {
excludePatterns,
pattern: includePatterns.length === 0 ? "." : includePatterns.length === 1 ? includePatterns[0] : `{${includePatterns.join(",")}}`,
};

function isNegatedGlob(glob: string) {
// https://github.com/micromatch/is-negated-glob/blob/master/index.js
return glob[0] === "!" && glob[1] !== "(";
}
}
4 changes: 4 additions & 0 deletions packages/common/src/runtimes/NodeRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export class NodeRuntime implements Runtime {
return process.env[name];
}

getEndOfLine() {
return require("os").EOL;
}

getPathMatchesPattern(path: string, pattern: string) {
return this.minimatch(path, pattern);
}
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/runtimes/Runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export interface Runtime {
path: RuntimePath;

getEnvVar(name: string): string | undefined;
getEndOfLine(): string;
getPathMatchesPattern(path: string, pattern: string): boolean;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/ts-morph/src/Project.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CompilerOptions, CompilerOptionsContainer, errors, FileSystemHost, FileUtils, InMemoryFileSystemHost, IterableUtils, matchGlobs, RealFileSystemHost,
ResolutionHostFactory, ScriptKind, StandardizedFilePath, TransactionalFileSystem, ts, TsConfigResolver } from "@ts-morph/common";
ResolutionHostFactory, runtime, ScriptKind, StandardizedFilePath, TransactionalFileSystem, ts, TsConfigResolver } from "@ts-morph/common";
import { CodeBlockWriter } from "./codeBlockWriter";
import { Diagnostic, EmitOptions, EmitResult, LanguageService, Node, Program, SourceFile, TypeChecker } from "./compiler";
import { Directory, DirectoryAddOptions } from "./fileSystem";
Expand Down Expand Up @@ -662,7 +662,7 @@ export class Project {
return ts.formatDiagnosticsWithColorAndContext(diagnostics.map(d => d.compilerObject), {
getCurrentDirectory: () => this._context.fileSystemWrapper.getCurrentDirectory(),
getCanonicalFileName: fileName => fileName,
getNewLine: () => opts.newLineChar ?? require("os").EOL,
getNewLine: () => opts.newLineChar ?? runtime.getEndOfLine(),
});
}

Expand Down

0 comments on commit 6608c25

Please sign in to comment.