Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(typescript-estree): cache project glob resolution#6367

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
JoshuaKGoldberg merged 6 commits intomainfromcache-glob-resolution
Jan 31, 2023
Merged
Show file tree
Hide file tree
Changes fromall 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
1 change: 1 addition & 0 deletions.cspell.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -72,6 +72,7 @@
"esquery",
"esrecurse",
"estree",
"globby",
"IDE's",
"IIFE",
"IIFEs",
Expand Down
5 changes: 5 additions & 0 deletions.eslintrc.js
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,6 +40,11 @@ module.exports = {
tsconfigRootDir: __dirname,
warnOnUnsupportedTypeScriptVersion: false,
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: false,
cacheLifetime: {
// we pretty well never create/change tsconfig structure - so need to ever evict the cache
// in the rare case that we do - just need to manually restart their IDE.
glob: 'Infinity',
},
},
rules: {
// make sure we're not leveraging any deprecated APIs
Expand Down
3 changes: 3 additions & 0 deletions.gitignore
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,6 +69,9 @@ jspm_packages/
# Editor-specific metadata folders
.vs

# nodejs cpu profiles
*.cpuprofile

.DS_Store
.idea
dist
Expand Down
11 changes: 11 additions & 0 deletionsdocs/architecture/Parser.mdx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,9 @@ The following additional configuration options are available by specifying them

```ts
interface ParserOptions {
cacheLifetime?: {
glob?: number | 'Infinity';
};
ecmaFeatures?: {
jsx?: boolean;
globalReturn?: boolean;
Expand All@@ -49,6 +52,14 @@ interface ParserOptions {
}
```

### `cacheLifetime`

This option allows you to granularly control our internal cache expiry lengths.

You can specify the number of seconds as an integer number, or the string 'Infinity' if you never want the cache to expire.

By default cache entries will be evicted after 30 seconds, or will persist indefinitely if the parser infers that it is a single run.

### `ecmaFeatures`

Optional additional options to describe how to parse the raw syntax.
Expand Down
35 changes: 34 additions & 1 deletiondocs/architecture/TypeScript-ESTree.mdx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -226,7 +226,23 @@ interface ParseAndGenerateServicesOptions extends ParseOptions {
allowAutomaticSingleRunInference?: boolean;

/**
* Path to a file exporting a custom ModuleResolver.
* Granular control of the expiry lifetime of our internal caches.
* You can specify the number of seconds as an integer number, or the string
* 'Infinity' if you never want the cache to expire.
*
* By default cache entries will be evicted after 30 seconds, or will persist
* indefinitely if `allowAutomaticSingleRunInference = true` AND the parser
* infers that it is a single run.
*/
cacheLifetime?: {
/**
* Glob resolution for `parserOptions.project` values.
*/
glob?: number | 'Infinity';
};

/**
* Path to a file exporting a custom `ModuleResolver`.
*/
moduleResolver?: string;
}
Expand DownExpand Up@@ -273,6 +289,23 @@ const { ast, services } = parseAndGenerateServices(code, {
});
```

##### `ModuleResolver`

The `moduleResolver` option allows you to specify the path to a module with a custom module resolver implementation. The module is expected to adhere to the following interface:

```ts
interface ModuleResolver {
version: 1;
resolveModuleNames(
moduleNames: string[],
containingFile: string,
reusedNames: string[] | undefined,
redirectedReference: ts.ResolvedProjectReference | undefined,
options: ts.CompilerOptions,
): (ts.ResolvedModule | undefined)[];
}
```

#### `parseWithNodeMaps(code, options)`

Parses the given string of code with the options provided and returns both the ESTree-compatible AST as well as the node maps.
Expand Down
13 changes: 12 additions & 1 deletionpackages/types/src/parser-options.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@ import type { Program } from 'typescript';
import type { Lib } from './lib';

type DebugLevel = boolean | ('typescript-eslint' | 'eslint' | 'typescript')[];
type CacheDurationSeconds = number | 'Infinity';

type EcmaVersion =
| 3
Expand DownExpand Up@@ -59,7 +60,17 @@ interface ParserOptions {
tsconfigRootDir?: string;
warnOnUnsupportedTypeScriptVersion?: boolean;
moduleResolver?: string;
cacheLifetime?: {
glob?: CacheDurationSeconds;
};

[additionalProperties: string]: unknown;
}

export { DebugLevel, EcmaVersion, ParserOptions, SourceType };
export {
CacheDurationSeconds,
DebugLevel,
EcmaVersion,
ParserOptions,
SourceType,
};
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ import type { CanonicalPath } from './shared';
import {
canonicalDirname,
createDefaultCompilerOptionsFromExtra,
createHash,
getCanonicalFileName,
getModuleResolver,
} from './shared';
Expand DownExpand Up@@ -105,19 +106,6 @@ function diagnosticReporter(diagnostic: ts.Diagnostic): void {
);
}

/**
* Hash content for compare content.
* @param content hashed contend
* @returns hashed result
*/
function createHash(content: string): string {
// No ts.sys in browser environments.
if (ts.sys?.createHash) {
return ts.sys.createHash(content);
}
return content;
}

function updateCachedFileList(
tsconfigPath: CanonicalPath,
program: ts.Program,
Expand Down
14 changes: 14 additions & 0 deletionspackages/typescript-estree/src/create-program/shared.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -124,12 +124,26 @@ function getModuleResolver(moduleResolverPath: string): ModuleResolver {
return moduleResolver;
}

/**
* Hash content for compare content.
* @param content hashed contend
* @returns hashed result
*/
function createHash(content: string): string {
// No ts.sys in browser environments.
if (ts.sys?.createHash) {
return ts.sys.createHash(content);
}
return content;
}

export {
ASTAndProgram,
CORE_COMPILER_OPTIONS,
canonicalDirname,
CanonicalPath,
createDefaultCompilerOptionsFromExtra,
createHash,
ensureAbsolutePath,
getCanonicalFileName,
getAstFromProgram,
Expand Down
69 changes: 69 additions & 0 deletionspackages/typescript-estree/src/parseSettings/ExpiringCache.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
import type { CacheDurationSeconds } from '@typescript-eslint/types';

export const DEFAULT_TSCONFIG_CACHE_DURATION_SECONDS = 30;
const ZERO_HR_TIME: [number, number] = [0, 0];

/**
* A map with key-level expiration.
*/
export class ExpiringCache<TKey, TValue> {
readonly #cacheDurationSeconds: CacheDurationSeconds;
/**
* The mapping of path-like string to the resolved TSConfig(s)
*/
protected readonly map = new Map<
TKey,
Readonly<{
value: TValue;
lastSeen: [number, number];
}>
>();

constructor(cacheDurationSeconds: CacheDurationSeconds) {
this.#cacheDurationSeconds = cacheDurationSeconds;
}

set(key: TKey, value: TValue): this {
this.map.set(key, {
value,
lastSeen:
this.#cacheDurationSeconds === 'Infinity'
? // no need to waste time calculating the hrtime in infinity mode as there's no expiry
ZERO_HR_TIME
: process.hrtime(),
});
return this;
}

get(key: TKey): TValue | undefined {
const entry = this.map.get(key);
if (entry?.value != null) {
if (this.#cacheDurationSeconds === 'Infinity') {
return entry.value;
}

const ageSeconds = process.hrtime(entry.lastSeen)[0];
if (ageSeconds < this.#cacheDurationSeconds) {
// cache hit woo!
return entry.value;
} else {
// key has expired - clean it up to free up memory
this.cleanupKey(key);
}
}
// no hit :'(
return undefined;
}

protected cleanupKey(key: TKey): void {
this.map.delete(key);
}

get size(): number {
return this.map.size;
}

clear(): void {
this.map.clear();
}
}
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
import debug from 'debug';
import { sync as globSync } from 'globby';
import isGlob from 'is-glob';

import type { CanonicalPath } from '../create-program/shared';
import {
ensureAbsolutePath,
getCanonicalFileName,
} from '../create-program/shared';
import { ensureAbsolutePath } from '../create-program/shared';
import type { TSESTreeOptions } from '../parser-options';
import type { MutableParseSettings } from './index';
import { inferSingleRun } from './inferSingleRun';
import { resolveProjectList } from './resolveProjectList';
import { warnAboutTSVersion } from './warnAboutTSVersion';

const log = debug(
Expand DownExpand Up@@ -98,23 +93,13 @@ export function createParseSettings(

// Providing a program overrides project resolution
if (!parseSettings.programs) {
const projectFolderIgnoreList = (
options.projectFolderIgnoreList ?? ['**/node_modules/**']
)
.reduce<string[]>((acc, folder) => {
if (typeof folder === 'string') {
acc.push(folder);
}
return acc;
}, [])
// prefix with a ! for not match glob
.map(folder => (folder.startsWith('!') ? folder : `!${folder}`));

parseSettings.projects = prepareAndTransformProjects(
tsconfigRootDir,
options.project,
projectFolderIgnoreList,
);
parseSettings.projects = resolveProjectList({
cacheLifetime: options.cacheLifetime,
project: options.project,
projectFolderIgnoreList: options.projectFolderIgnoreList,
singleRun: parseSettings.singleRun,
tsconfigRootDir: tsconfigRootDir,
});
}

warnAboutTSVersion(parseSettings);
Expand DownExpand Up@@ -144,58 +129,3 @@ function enforceString(code: unknown): string {
function getFileName(jsx?: boolean): string {
return jsx ? 'estree.tsx' : 'estree.ts';
}

function getTsconfigPath(
tsconfigPath: string,
tsconfigRootDir: string,
): CanonicalPath {
return getCanonicalFileName(
ensureAbsolutePath(tsconfigPath, tsconfigRootDir),
);
}

/**
* Normalizes, sanitizes, resolves and filters the provided project paths
*/
function prepareAndTransformProjects(
tsconfigRootDir: string,
projectsInput: string | string[] | undefined,
ignoreListInput: string[],
): CanonicalPath[] {
const sanitizedProjects: string[] = [];

// Normalize and sanitize the project paths
if (typeof projectsInput === 'string') {
sanitizedProjects.push(projectsInput);
} else if (Array.isArray(projectsInput)) {
for (const project of projectsInput) {
if (typeof project === 'string') {
sanitizedProjects.push(project);
}
}
}

if (sanitizedProjects.length === 0) {
return [];
}

// Transform glob patterns into paths
const nonGlobProjects = sanitizedProjects.filter(project => !isGlob(project));
const globProjects = sanitizedProjects.filter(project => isGlob(project));
const uniqueCanonicalProjectPaths = new Set(
nonGlobProjects
.concat(
globSync([...globProjects, ...ignoreListInput], {
cwd: tsconfigRootDir,
}),
)
.map(project => getTsconfigPath(project, tsconfigRootDir)),
);

log(
'parserOptions.project (excluding ignored) matched projects: %s',
uniqueCanonicalProjectPaths,
);

return Array.from(uniqueCanonicalProjectPaths);
}
2 changes: 1 addition & 1 deletionpackages/typescript-estree/src/parseSettings/index.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -98,7 +98,7 @@ export interface MutableParseSettings {
/**
* Normalized paths to provided project paths.
*/
projects: CanonicalPath[];
projects:readonlyCanonicalPath[];

/**
* Whether to add the `range` property to AST nodes.
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp