Skip to content

⬅️ Back to Table of Contents

📄 createProjectService

📊 Analysis Summary

Metric Count
🔧 Functions 8
📦 Imports 3
📊 Variables & Constants 1
🔄 Re-exports 1
📐 Interfaces 2
📑 Type Aliases 1

📚 Table of Contents

🛠️ File Location:

📂 packages/project-service/src/createProjectService.ts

📦 Imports

Name Source
ProjectServiceOptions @typescript-eslint/types
debug debug
getParsedConfigFileFromTSServer ./getParsedConfigFileFromTSServer.js

Variables & Constants

Name Type Kind Value Exported
DEFAULT_PROJECT_MATCHED_FIL... 8 const 8

Re-exports

Type Source Exported Names
named @typescript-eslint/types ProjectServiceOptions

Functions

createProjectService({ host, jsDocParsingMode, optio…: CreateProjectServiceSettings): ProjectServiceAndMetadata

Creates a new Project Service instance, as well as metadata on its creation.

Parameters:

  • settings any: Settings to create a new Project Service instance.

Returns: undefined A new Project Service instance, as well as metadata on its creation.

Examples:

```ts
import { createProjectService } from '@typescript-eslint/project-service';

const { service } = createProjectService();

service.openClientFile('index.ts');
<details><summary>Raw JSDoc</summary>

```typescript
/**
 * Creates a new Project Service instance, as well as metadata on its creation.
 * @param settings Settings to create a new Project Service instance.
 * @returns A new Project Service instance, as well as metadata on its creation.
 * @example
 * ```ts
 * import { createProjectService } from '@typescript-eslint/project-service';
 *
 * const { service } = createProjectService();
 *
 * service.openClientFile('index.ts');
 * ```
 */

Calls:

  • require
  • this.msg
  • logTsserverErr
  • logTsserverPerf
  • logTsserverInfo
  • log
  • logTsserverEvent
  • service.setHostConfiguration
  • getParsedConfigFileFromTSServer (from ./getParsedConfigFileFromTSServer.js)
  • service.setCompilerOptionsForInferredProjects
  • performance.now

Internal Comments:

// We import this lazily to avoid its cost for users who don't use the service (x2)
// TODO: Once we drop support for TS<5.3 we can import from "typescript" directly (x2)
// eslint-disable-next-line @typescript-eslint/no-require-imports (x2)
// TODO: see getWatchProgramsForProjects (x2)
// We don't watch the disk, we just refer to these when ESLint calls us (x2)
// there's a whole separate update pass in maybeInvalidateProgram at the bottom of getWatchProgramsForProjects (x2)
// (this "goes nuclear on TypeScript") (x2)
// We stop loading any TypeScript plugins by default, to prevent them from attaching disk watchers
// See https://github.com/typescript-eslint/typescript-eslint/issues/9905
// The debug library doesn't use levels without creating a namespace for each. (x2)
// Log levels are not passed to the writer so we wouldn't be able to forward (x2)
// to a respective namespace.  Supporting would require an additional flag for (x2)
// granular control.  Defaulting to all levels for now. (x2)
// if none of the debug namespaces are enabled, then don't enable logging in tsserver (x4)
// NOTE: The inferred projects API is not intended for source files when a tsconfig (x3)
// exists. There is no API that generates an InferredProjectCompilerOptions suggesting (x3)
// it is meant for hard coded options passed in. Hard asserting as a work around. (x3)
// See https://github.com/microsoft/TypeScript/blob/27bcd4cb5a98bce46c9cdd749752703ead021a4b/src/server/protocol.ts#L1904 (x3)

Code
export function createProjectService({
  host,
  jsDocParsingMode,
  options: optionsRaw = {},
  tsconfigRootDir,
}: CreateProjectServiceSettings = {}): ProjectServiceAndMetadata {
  const options = {
    defaultProject: 'tsconfig.json',
    ...optionsRaw,
  };

  // We import this lazily to avoid its cost for users who don't use the service
  // TODO: Once we drop support for TS<5.3 we can import from "typescript" directly
  // eslint-disable-next-line @typescript-eslint/no-require-imports
  const tsserver = require('typescript/lib/tsserverlibrary') as typeof ts;

  // TODO: see getWatchProgramsForProjects
  // We don't watch the disk, we just refer to these when ESLint calls us
  // there's a whole separate update pass in maybeInvalidateProgram at the bottom of getWatchProgramsForProjects
  // (this "goes nuclear on TypeScript")
  const system: ts.server.ServerHost = {
    ...tsserver.sys,
    clearImmediate,
    clearTimeout,
    setImmediate,
    setTimeout,
    watchDirectory: createStubFileWatcher,
    watchFile: createStubFileWatcher,

    // We stop loading any TypeScript plugins by default, to prevent them from attaching disk watchers
    // See https://github.com/typescript-eslint/typescript-eslint/issues/9905
    ...(!options.loadTypeScriptPlugins && {
      require: () => ({
        error: {
          message:
            'TypeScript plugins are not required when using parserOptions.projectService.',
        },
        module: undefined,
      }),
    }),
    ...host,
  };

  const logger: ts.server.Logger = {
    close: doNothing,
    endGroup: doNothing,
    getLogFileName: (): undefined => undefined,
    // The debug library doesn't use levels without creating a namespace for each.
    // Log levels are not passed to the writer so we wouldn't be able to forward
    // to a respective namespace.  Supporting would require an additional flag for
    // granular control.  Defaulting to all levels for now.
    hasLevel: (): boolean => true,
    info(s) {
      this.msg(s, tsserver.server.Msg.Info);
    },
    loggingEnabled: (): boolean =>
      // if none of the debug namespaces are enabled, then don't enable logging in tsserver
      logTsserverInfo.enabled ||
      logTsserverErr.enabled ||
      logTsserverPerf.enabled,
    msg: (s, type) => {
      switch (type) {
        case tsserver.server.Msg.Err:
          logTsserverErr(s);
          break;
        case tsserver.server.Msg.Perf:
          logTsserverPerf(s);
          break;
        default:
          logTsserverInfo(s);
      }
    },
    perftrc(s) {
      this.msg(s, tsserver.server.Msg.Perf);
    },
    startGroup: doNothing,
  };

  log('Creating Project Service with: %o', options);

  const service = new tsserver.server.ProjectService({
    cancellationToken: { isCancellationRequested: (): boolean => false },
    eventHandler: logTsserverEvent.enabled
      ? (e): void => {
          logTsserverEvent(e);
        }
      : undefined,
    host: system,
    jsDocParsingMode,
    logger,
    session: undefined,
    useInferredProjectPerProjectRoot: false,
    useSingleInferredProject: false,
  });

  service.setHostConfiguration({
    preferences: {
      includePackageJsonAutoImports: 'off',
    },
  });

  log('Enabling default project: %s', options.defaultProject);

  const configFile = getParsedConfigFileFromTSServer(
    tsserver,
    options.defaultProject,
    !!optionsRaw.defaultProject,
    tsconfigRootDir,
  );

  if (configFile) {
    service.setCompilerOptionsForInferredProjects(
      // NOTE: The inferred projects API is not intended for source files when a tsconfig
      // exists. There is no API that generates an InferredProjectCompilerOptions suggesting
      // it is meant for hard coded options passed in. Hard asserting as a work around.
      // See https://github.com/microsoft/TypeScript/blob/27bcd4cb5a98bce46c9cdd749752703ead021a4b/src/server/protocol.ts#L1904
      configFile.options as ts.server.protocol.InferredProjectCompilerOptions,
    );
  }

  return {
    allowDefaultProject: options.allowDefaultProject,
    lastReloadTimestamp: performance.now(),
    maximumDefaultProjectFileMatchCount:
      options.maximumDefaultProjectFileMatchCount_THIS_WILL_SLOW_DOWN_LINTING ??
      DEFAULT_PROJECT_MATCHED_FILES_THRESHOLD,
    service,
  };
}

doNothing(): void

Returns: void

Code
(): void => {}

createStubFileWatcher(): ts.FileWatcher

Returns: ts.FileWatcher

Code
(): ts.FileWatcher => ({
  close: doNothing,
})

Internal helpers

Declared inside another function in this file.

require(): { error: { message: string; }; module: any; }

Returns: { error: { message: string; }; module: any; }

Code
() => ({
        error: {
          message:
            'TypeScript plugins are not required when using parserOptions.projectService.',
        },
        module: undefined,
      })

getLogFileName(): undefined

Returns: undefined

Code
(): undefined => undefined

hasLevel(): boolean

Returns: boolean

Code
(): boolean => true

loggingEnabled(): boolean

Returns: boolean

Internal Comments:

// if none of the debug namespaces are enabled, then don't enable logging in tsserver

Code
(): boolean =>
      // if none of the debug namespaces are enabled, then don't enable logging in tsserver
      logTsserverInfo.enabled ||
      logTsserverErr.enabled ||
      logTsserverPerf.enabled

msg(s: any, type: any): void

Parameters:

  • s any
  • type any

Returns: void

Calls:

  • logTsserverErr
  • logTsserverPerf
  • logTsserverInfo
Code
(s, type) => {
      switch (type) {
        case tsserver.server.Msg.Err:
          logTsserverErr(s);
          break;
        case tsserver.server.Msg.Perf:
          logTsserverPerf(s);
          break;
        default:
          logTsserverInfo(s);
      }
    }

Interfaces

ProjectServiceAndMetadata

Interface Code
export interface ProjectServiceAndMetadata {
  /**
   * Files allowed to be loaded from the default project, if any were specified.
   */
  allowDefaultProject: string[] | undefined;

  /**
   * The performance.now() timestamp of the last reload of the project service.
   */
  lastReloadTimestamp: number;

  /**
   * The maximum number of files that can be matched by the default project.
   */
  maximumDefaultProjectFileMatchCount: number;

  /**
   * The created TypeScript Project Service instance.
   */
  service: TypeScriptProjectService;
}

Properties

Name Type Optional Description
allowDefaultProject string[] \| undefined not shown
lastReloadTimestamp number not shown
maximumDefaultProjectFileMatchCount number not shown
service TypeScriptProjectService not shown

CreateProjectServiceSettings

Interface Code
export interface CreateProjectServiceSettings {
  /**
   * Granular options to configure the project service.
   */
  options?: ProjectServiceOptions;

  /**
   * How aggressively (and slowly) to parse JSDoc comments.
   */
  jsDocParsingMode?: ts.JSDocParsingMode;

  /**
   * Root directory for the tsconfig.json file, if not the current directory.
   */
  tsconfigRootDir?: string;

  /**
   * Custom project service host.
   *
   * @default `ts.sys` with stub watchers
   */
  host?: Partial<ts.server.ServerHost>;
}

Properties

Name Type Optional Description
options ProjectServiceOptions not shown
jsDocParsingMode ts.JSDocParsingMode not shown
tsconfigRootDir string not shown
host Partial<ts.server.ServerHost> not shown

Type Aliases

TypeScriptProjectService

/* * Shortcut type to refer to TypeScript's server ProjectService. /

type TypeScriptProjectService = ts.server.ProjectService;

Generated by Syntax Scribe