UNPKG

alm

Version:

The best IDE for TypeScript

745 lines (669 loc) 16.9 kB
/** * Its Types (e.g. enums) + constants :) */ export const cacheDir = './.alm'; export const title = "Application Lifecycle Management tools for TypeScript"; export enum TriState { Unknown, True, False, } export const errors = { CALLED_WHEN_NO_ACTIVE_PROJECT_FOR_FILE_PATH: "A query *that needs an active project* was made when there is no active project for given filePath", CALLED_WHEN_NO_ACTIVE_PROJECT_GLOBAL: "A query *that needs an active project* was made when there is no active project" } /** * Some session constants */ /** When a new server stats up */ export const urlHashNormal = "root"; /** When user requests a new window */ export const urlHashNewSession = "new-session"; /** When alm is started ni debug mode */ export const urlHashDebugSession = "debug"; /** * FARM : Don't want to crash by running out of memory / ui preference */ export const maxCountFindAndReplaceMultiResults = 1000; export interface FilePathPosition { filePath: string; position: EditorPosition; } /** * Session related types */ export interface SessionsFileContents { sessions: SessionOnDisk[]; /** Relative path to tsconfig.json including file name */ relativePathToTsconfig?: string; } export interface SessionOnDisk { /** unique to each session */ id: string; /** the tabs the user has open */ tabLayout: TabLayoutOnDisk; selectedTabId: string | null; /** Duration since epoch */ lastUsed: number; /** * NOTE: there can be any number of other settings that are type checked only at the client */ } /** * Same as a `TabInstance` but works with `relativeUrl` */ export interface TabInstanceOnDisk { id: string; relativeUrl: string; /** Any additional data that the tab wants to serialize */ additionalData: any; } /** * What the main application tab container knows about a tab */ export interface TabInstance { id: string; url: string; /** Any additional data that the tab wants to serialize */ additionalData: any; } /** * Just the layout information we serialize * A recursive structure for re-storing tab information */ export type TabLayout = { type: 'stack' | 'row' | 'column' | string; /** out of 100 */ width: number; /** out of 100 */ height: number; /** Only exist on a `stack` */ tabs: TabInstance[]; activeItemIndex: number; /** Only exists if type is not `stack` */ subItems: TabLayout[]; } /** Same as above with `ui` stuff replaced with `disk` stuff */ export type TabLayoutOnDisk = { type: 'stack' | 'row' | 'column' | string; /** out of 100 */ width: number; /** out of 100 */ height: number; /** Only exist on a `stack` */ tabs: TabInstanceOnDisk[]; activeItemIndex: number; /** Only exists if type is not `stack` */ subItems: TabLayoutOnDisk[]; } /** * Refactoring related stuff */ export interface Refactoring extends ts.TextChange { filePath: string; } /** * Because you generally want to transact per file * You don't need to create this manually. Just use `getRefactoringsByFilePath` */ export interface RefactoringsByFilePath { [filePath: string]: Refactoring[]; } /** * Reason is we want to transact by file path * Also, this function sorts per file so you can apply refactorings in order 🌹 */ export function getRefactoringsByFilePath(refactorings: Refactoring[]) { var loc: RefactoringsByFilePath = {}; for (let refac of refactorings) { if (!loc[refac.filePath]) loc[refac.filePath] = []; loc[refac.filePath].push(refac); } // sort each of these in descending by start location for (let filePath in loc) { let refactorings = loc[filePath]; refactorings.sort((a: Refactoring, b: Refactoring) => { return (b.span.start - a.span.start); }); } return loc; } /** * For file listing we like to know if its a dir or file */ export enum FilePathType { File, Dir } export interface FilePath { filePath: string; type: FilePathType } /** For incremental buffered file listing changes */ export interface FileListingDelta { addedFilePaths: FilePath[]; removedFilePaths: FilePath[]; } /** * File model stuff */ export interface FileStatus { filePath: string; saved: boolean; eol: string; } /** * Project JS File status stuff */ export interface JSOutputStatus { /** Its convinient to have it hare */ inputFilePath: string; /** One of the various states */ state: JSOutputState; /** Only if the state is for some JS file */ outputFilePath?: string; } /** The JS file can only be in one of these states */ export enum JSOutputState { /** If emit skipped (Either emit is blocked or compiler options are noEmit) or perhaps there isn't a JS file emit for this (e.g .d.ts files) */ NoJSFile = 1, /** If JS file then its one of these */ JSUpToDate, JSOutOfDate, } export type JSOutputStatusCache = { [inputFilePath: string]: JSOutputStatus } export type LiveBuildResults = { builtCount: number; totalCount: number; } /** Query response for individual file query */ export type GetJSOutputStatusResponse = { inActiveProject: boolean, /** Only present if the file as in active project */ outputStatus?: JSOutputStatus }; /** * Complete related stuff */ /** Some constants */ export const completionKindSnippet = "snippet"; export const completionKindPath = "path"; /** A completion */ export interface Completion { /** stuff like ("var"|"method"etc) | "snippet" | "path" etc */ kind: string; /** stuff like "toString", "./relativePath" */ name: string; /** * This is displayParts (for functions). Empty for `var` etc. */ display?: string; /** * the docComment if any * Also: `fullPath` for path ;) */ comment?: string; /** Only valid if `kind` is snippet */ insertText?: string; /** Only valid if `kind` is path completion */ textEdit?: CodeEdit; } /** * Really only used when moving data around. * We still map it to `Completion` before we handing it over for *autocomplete* */ export interface PathCompletion { fileName: string; relativePath: string; fullPath: string; } export interface PathCompletionForAutocomplete extends PathCompletion { pathStringRange: { from: number, to: number, } } /** * Editor Config stuff */ export interface EditorOptions { tabSize: number; newLineCharacter: string; convertTabsToSpaces: boolean; trimTrailingWhitespace: boolean; insertFinalNewline: boolean; } /** * TSConfig details */ /** * These are the projects that the user can select from. * Just the name and config path really */ export interface AvailableProjectConfig { name: string; /** Virtual projects are projects rooted at some `.ts`/`.js` file */ isVirtual: boolean; /** If the project is virtual than this will point to a `.ts`/`.js` file */ tsconfigFilePath: string; } /** * Project Data : the config file + all the file path contents */ export interface FilePathWithContent { filePath: string; contents: string; } export interface ProjectDataLoaded { configFile: TypeScriptConfigFileDetails; filePathWithContents: FilePathWithContent[]; } /** * Our analysis of stuff we want from package.json */ export interface PackageJsonParsed { /** We need this as this is the name the user is going to import **/ name: string; /** we need this to figure out the basePath (will depend on how `outDir` is relative to this directory) */ directory: string; /** This is going to be typescript.definition */ definition: string; main: string; } /** * This is `TypeScriptProjectRawSpecification` parsed further * Designed for use throughout out code base */ export interface TsconfigJsonParsed { compilerOptions: ts.CompilerOptions; files: string[]; typings: string[]; // These are considered externs for .d.ts. Note : duplicated in files formatCodeOptions: ts.FormatCodeOptions; compileOnSave: boolean; buildOnSave: boolean; package?: PackageJsonParsed; } export interface TypeScriptConfigFileDetails { /** The path to the project file. This acts as the baseDIR */ projectFileDirectory: string; /** The actual path of the project file (including tsconfig.json) or srcFile if `inMemory` is true */ projectFilePath: string; project: TsconfigJsonParsed; inMemory: boolean; } /** * Git types */ /** Note : 0,2 means lines 0,1,2 */ export type GitDiffSpan = { from: number; to: number; } export type GitDiff = { added: GitDiffSpan[]; removed: number[]; modified: GitDiffSpan[]; } export type GitAddAllCommitAndPushQuery = { message: string; } export type GitAddAllCommitAndPushResult = { type: 'error'; error: string; } | { type: 'success' log: string; }; /** * Errors */ export enum ErrorsDisplayMode { all = 1, openFiles = 2, } /** * Documentation related stuff */ /** for project symbols view */ export interface NavigateToItem { name: string; kind: string; filePath: string; position: EditorPosition; fileName: string; } export interface GetNavigateToItemsResponse { items: NavigateToItem[]; } /** * The TypeDoc icons a pretty expansive 🌹 with a few ideas that I disagree with / or think are too difficult. * E.g the type `event`. The "grey" coloring of the global functions. The following is a simpler subset. * * Places that need to be kept in sync: * - typeIcon.tsx: the location in typeIcons.svg * - the legend component * - the server responses */ export enum IconType { /** * There can be only one global * Any of the remaining things can be either in a module or global */ Global, Namespace, // same for module Variable, Function, FunctionGeneric, Enum, EnumMember, Interface, InterfaceGeneric, InterfaceConstructor, InterfaceProperty, InterfaceMethod, InterfaceMethodGeneric, InterfaceIndexSignature, Class, ClassGeneric, ClassConstructor, ClassProperty, ClassMethod, ClassMethodGeneric, ClassIndexSignature, } /** * The documentation model * We have * - global * - modules * * These are just "name" + containers for OtherThings * * OtherThings are just: * - class * - namespace * - interface / type * - enum * * Where Namespace is just a "name" container for OtherThings */ export interface DocumentedType { name: string; icon: IconType, comment: string, subItems: DocumentedType[]; location: DocumentedTypeLocation; } export type DocumentedTypeLocation = FilePathPosition; /** For top level module names */ export interface GetTopLevelModuleNamesResponse { /** Present in our project */ files: DocumentedType[]; } /** * * * * UML View * * * */ /** Class */ export interface UMLClass { // Similar to Documented type. name: string; icon: IconType location: DocumentedTypeLocation; // Unlike DocumentedType.subItems we have `members` members: UMLClassMember[]; // Also extends if any extends?: UMLClass; } export enum UMLClassMemberVisibility { Public = 1, Private, Protected } export enum UMLClassMemberLifetime { Instance = 1, Static } export interface UMLClassMember { name: string icon: IconType; location: DocumentedTypeLocation; visibility: UMLClassMemberVisibility; lifetime: UMLClassMemberLifetime; /** Default is false */ override?: UMLClassMember; } /** * Ts Flow types */ /** * Get the root ts flow points */ export interface TsFlowRootQuery { filePath: string; } export interface TsFlowPoint { filePath: string; from: EditorPosition; to: EditorPosition; displayName: string; } export interface TsFlowRootResponse { flowPoints: TsFlowPoint[]; } /** * Live Analysis * e.g. when a member overrides a parent we show a hint. */ export interface LiveAnalysisQuery { filePath: string; } export interface LiveAnalysisResponse { overrides: LiveAnalysisOverrideInfo[] } export interface LiveAnalysisOverrideInfo { line: number; overrides: UMLClassMember; } /** * Monaco command pallete support */ export interface MonacoActionInformation { label: string, id: string; kbd: string | null; } /** * When a worker is *working* it can send us a message */ export type Working = { working: boolean } /** * Tested */ export enum TestStatus { NotRunYet = 1, Fail, Success, Skipped, } export type TestErrorStack = FilePathPosition[]; export type TestLogPosition = { lastPositionInFile: EditorPosition; isActualLastInFile: boolean; stack: TestErrorStack; } export type TestError = { testLogPosition: TestLogPosition; message: string; stack: TestErrorStack; } export type TestResult = { description: string; status: TestStatus; testLogPosition: TestLogPosition; /** None if skipped */ durationMs?: number; /** Only in case of test failure */ error?: TestError; } export type TestSuiteResult = { description: string; testLogPosition: TestLogPosition; stats: TestContainerStats; /** Can have other TestSuites or Tests */ suites: TestSuiteResult[]; tests: TestResult[]; } export type TestLog = { /** * The log might not be pointing to the same file. We should still show it against * `this` spec execution */ testLogPosition: TestLogPosition; /** * Arguments. * Note: they will be stringified and unstringified by the time they make it to the UI */ args: any[]; } /** The root of any testing system is a test file */ export type TestModule = { filePath: string; /** From instrumentation */ logs: TestLog[]; /** * Also contained in the `suites` * But raised up for better module level overview */ testResults: TestResult[]; /** Present once its been run */ suites: TestSuiteResult[]; stats: TestContainerStats; } /** Both modules and suites are test containers and have these stats */ export type TestContainerStats = { testCount: number; passCount: number; failCount: number; skipCount: number; /** milliseconds */ durationMs: number; }; export type TestSuitesByFilePath = { [filePath: string]: TestModule; } /** We just push the modules that have updated */ export type TestResultsDelta = { updatedModuleMap: TestSuitesByFilePath, clearedModules: string[]; initial: boolean; } export type TestSuitePosition = { title: string; /** * The last origin in the file */ testLogPosition: TestLogPosition; } export type TestItPosition = { title: string; /** * The last origin in the file */ testLogPosition: TestLogPosition; } ////////////////////// // Error cache ////////////////////// export type CodeErrorSource = 'tsconfig' | 'projectService' | 'linter' | 'tested' export interface CodeError { source: CodeErrorSource; filePath: string; from: EditorPosition; to: EditorPosition; message: string; preview: string; level: 'warning' | 'error'; } export interface ErrorsByFilePath { [filePath: string]: CodeError[]; } /** * We don't send all the errors to front end continuously. * But we do still tell the total count. */ export interface LimitedErrorsUpdate { errorsByFilePath: ErrorsByFilePath; totalCount: number; syncCount: number; tooMany: boolean; } /** * Allows true syncing of one cache with another */ export type ErrorCacheDelta = { added: ErrorsByFilePath; removed: ErrorsByFilePath; initial: boolean; } /** Lots of things don't have a good error. But we would like to be consistent even with simple errors */ export function makeBlandError(filePath: string, error: string, source: CodeErrorSource): CodeError { return { source, filePath, from: { line: 0, ch: 0 }, to: { line: 0, ch: 0 }, message: error, preview: null, level: 'error' } } ////////////////////// // Live Demo ////////////////////// export type LiveDemoData = | { type: 'start' } | { type: 'data' data: string } | { type: 'end' code: number }; ////////////////////// // Live react demo ////////////////////// export const liveDemoMountUrl = '/demo'; export type LiveDemoBundleResult = | { type: 'bundling' } | { type: 'success' } | { type: 'error', error: string }