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

chore: add performance package with a project service hyperfine comparison#7870

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

Closed
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.eslintignore
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ coverage
__snapshots__
.docusaurus
build
packages/performance/generated

# Files copied as part of the build
packages/types/src/generated/**/*.ts
Expand Down
23 changes: 23 additions & 0 deletions.github/workflows/performance.yml
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
name: 'Performance Testing'

on: workflow_dispatch

jobs:
comparison:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/prepare-install
- run: brew install hyperfine
- run: PERFORMANCE_FILE_COUNT=${{ matrix.file-count }} yarn workspace @typescript-eslint/performance run generate
- run: hyperfine yarn workspace @typescript-eslint/performance run test:${{ matrix.test}}
strategy:
matrix:
file-count: [50, 100, 150, 200]
test: ['project', 'service']
summary:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/prepare-install
- run: yarn workspace @typescript-eslint/performance run test:summary
5 changes: 4 additions & 1 deletion.gitignore
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,9 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Performance testing
packages/performance/generated

# Website
packages/website/.docusaurus
packages/website/.cache-loader
Expand DownExpand Up@@ -95,4 +98,4 @@ packages/types/src/generated/**/*.ts
!.yarn/sdks
!.yarn/versions

.nx/cache
.nx/cache
15 changes: 15 additions & 0 deletionspackages/performance/.eslintrc.cjs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
/* eslint-env node */
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/strict-type-checked',
'plugin:@typescript-eslint/stylistic-type-checked',
],
plugins: ['@typescript-eslint'],
parser: '@typescript-eslint/parser',
parserOptions: {
project: true,
tsconfigRootDir: __dirname,
},
root: true,
};
27 changes: 27 additions & 0 deletionspackages/performance/README.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
# Performance Testing

A quick internal utility for testing lint performance.

## Generating a Summary

`test:summary` runs the three performance scenarios under different file counts, then prints a summary table:

```shell
yarn test:summary
```

For each of those file counts, the generated code is 50% `.js` and 50% `.ts`.

## Running Tests Manually

First generate the number of files you'd like to test:

```shell
PERFORMANCE_FILE_COUNT=123 yarn generate
```

Then run one or more of the test types:

- `yarn test:project`: Traditional `project: true`
- `yarn test:service` `EXPERIMENTAL_useProjectService`
- `yarn test:service:seeded`: `EXPERIMENTAL_useProjectService`, with `allowDefaultProjectFallbackFilesGlobs`
56 changes: 56 additions & 0 deletionspackages/performance/generate.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
import * as fs from 'fs/promises';
import * as path from 'path';

import {
generateESLintConfig,
generateJsFile,
generateTSConfig,
generateTsFile,
} from './generating.js';
import { directory, writeFile } from './writing.js';

const fileCount = Number(process.env.PERFORMANCE_FILE_COUNT);
if (!fileCount) {
throw new Error(
`Invalid process.env.PERFORMANCE_FILE_COUNT: ${process.env.PERFORMANCE_FILE_COUNT}`,
);
}

await fs.rm(directory, { force: true, recursive: true });
await fs.mkdir(path.join(directory, 'src'), { recursive: true });

// The root-level TSConfig is used for the project service, which intentionally
// uses the default/inferred project for non-included JS files.
await writeFile(`tsconfig.json`, generateTSConfig(false));

// The explicit project allows JS, akin to the common tsconfig.eslint.json.
await writeFile(`tsconfig.project.json`, generateTSConfig(true));

for (const [alias, parserOptions] of [
['project', `project: "./tsconfig.project.json"`],
['service', `EXPERIMENTAL_useProjectService: true`],
[
'service.seeded',
`allowDefaultProjectFallbackFilesGlobs: ["./src/*.js"],\n EXPERIMENTAL_useProjectService: true`,
],
] as const) {
await writeFile(
`.eslintrc.${alias}.cjs`,
generateESLintConfig(parserOptions),
);
}

for (let i = 0; i < fileCount; i += 1) {
const [extension, generator] =
i % 2 ? ['js', generateJsFile] : ['ts', generateTsFile];

await writeFile(`src/example${i}.${extension}`, generator(i));
}

await writeFile(
'src/index.ts',
new Array(fileCount)
.fill(undefined)
.map((_, i) => `export { example${i} } from "./example${i}.js";`)
.join('\n'),
);
48 changes: 48 additions & 0 deletionspackages/performance/generating.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
export function generateESLintConfig(parserOptions: string) {
return `
/* eslint-env node */
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/strict-type-checked',
'plugin:@typescript-eslint/stylistic-type-checked',
],
plugins: ['@typescript-eslint'],
parser: '@typescript-eslint/parser',
parserOptions: {
${parserOptions},
tsconfigRootDir: __dirname
},
root: true,
};
`.trimStart();
}

export function generateJsFile(i: number) {
return `
export async function example${i}(input) {
return typeof input === 'number' ? input.toPrecision(1) : input.toUpperCase();
}
`.trimStart();
}

export function generateTsFile(i: number) {
return `
export async function example${i}<T extends number | string>(input: T) {
return typeof input === 'number' ? input.toPrecision(1) : input.toUpperCase();
}
`.trimStart();
}

export function generateTSConfig(allowJs: boolean) {
return `
{
"compilerOptions": {
"allowJs": ${allowJs},
"module": "ESNext",
"strict": true,
"target": "ESNext"
}
}
`.trimStart();
}
28 changes: 28 additions & 0 deletionspackages/performance/package.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
{
"name": "@typescript-eslint/performance",
"version": "6.9.1",
"private": true,
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/typescript-eslint/typescript-eslint.git",
"directory": "packages/performance"
},
"scripts": {
"generate": "tsx ./generate.ts",
"generate:watch": "tsx --watch ./generate.ts",
"test:project": "time eslint --config ./generated/.eslintrc.project.cjs ./generated || true",
"test:service": "time eslint --config ./generated/.eslintrc.service.cjs ./generated || true",
"test:service:seeded": "time eslint --config ./generated/.eslintrc.service.seeded.cjs ./generated || true",
"test:summary": "tsx ./summary.ts"
},
"dependencies": {
"@typescript-eslint/eslint-plugin": "6.9.1",
"@typescript-eslint/parser": "6.9.1"
},
"devDependencies": {
"eslint": "*",
"execa": "*",
"tsx": "*"
}
}
17 changes: 17 additions & 0 deletionspackages/performance/summary.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
import { $ } from 'execa';

const getTiming = ({ stderr }: { stderr: string }) =>
stderr.trim().replace(/ +/g, ' ');

const counts: Record<string, unknown> = {};

for (const fileCount of [5, 10, 25, 50, 75, 100, 125, 150, 175, 200]) {
await $({ env: { PERFORMANCE_FILE_COUNT: `${fileCount}` } })`yarn generate`;
const project = getTiming(await $`yarn test:project`);
const service = getTiming(await $`yarn test:service`);
const serviceSeeded = getTiming(await $`yarn test:service:seeded`);

counts[fileCount] = { project, service, 'service (seeded)': serviceSeeded };
}

console.table(counts);
8 changes: 8 additions & 0 deletionspackages/performance/tsconfig.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"target": "ESNext"
}
}
8 changes: 8 additions & 0 deletionspackages/performance/writing.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
import * as fs from 'fs/promises';
import * as path from 'path';

export const directory = 'generated';

export async function writeFile(fileName: string, data: string) {
await fs.writeFile(path.join(directory, fileName), data);
}
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
/* eslint-disable @typescript-eslint/no-empty-function -- for TypeScript APIs*/
import * as fs from 'fs';
import { sync as globSync } from 'globby';
import type * as ts from 'typescript/lib/tsserverlibrary';

const doNothing = (): void => {};
Expand All@@ -9,7 +11,9 @@ const createStubFileWatcher = (): ts.FileWatcher => ({

export type TypeScriptProjectService = ts.server.ProjectService;

export function createProjectService(): TypeScriptProjectService {
export function createProjectService(
allowDefaultProjectFallbackFilesGlobs: string[] = [],
): TypeScriptProjectService {
// We import this lazily to avoid its cost for users who don't use the service
const tsserver = require('typescript/lib/tsserverlibrary') as typeof ts;

Expand All@@ -27,7 +31,7 @@ export function createProjectService(): TypeScriptProjectService {
watchFile: createStubFileWatcher,
};

return new tsserver.server.ProjectService({
const service = new tsserver.server.ProjectService({
host: system,
cancellationToken: { isCancellationRequested: (): boolean => false },
useSingleInferredProject: false,
Expand All@@ -45,5 +49,13 @@ export function createProjectService(): TypeScriptProjectService {
},
session: undefined,
});

for (const filesGlob of allowDefaultProjectFallbackFilesGlobs) {
for (const fileName of globSync(filesGlob, { ignore: ['node_modules/'] })) {
service.openClientFile(fileName, fs.readFileSync(fileName).toString());
}
}

return service;
}
/* eslint-enable @typescript-eslint/no-empty-function */
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,9 @@ export function createParseSettings(
process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER !== 'false') ||
(process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER === 'true' &&
options.EXPERIMENTAL_useProjectService !== false)
? (TSSERVER_PROJECT_SERVICE ??= createProjectService())
? (TSSERVER_PROJECT_SERVICE ??= createProjectService(
options.allowDefaultProjectFallbackFilesGlobs,
))
: undefined,
EXPERIMENTAL_useSourceOfProjectReferenceRedirect:
options.EXPERIMENTAL_useSourceOfProjectReferenceRedirect === true,
Expand Down
10 changes: 10 additions & 0 deletionspackages/typescript-estree/src/parser-options.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -103,6 +103,16 @@ interface ParseAndGenerateServicesOptions extends ParseOptions {
*/
EXPERIMENTAL_useProjectService?: boolean;

/**
* ***EXPERIMENTAL FLAG*** - Use this at your own risk.
*
* If `EXPERIMENTAL_useProjectService` is true, which files to load into the service immediately.
* Intentionally an annoyingly long name for now. We'll think of a better one if we need.
*
* @see https://github.com/typescript-eslint/typescript-eslint/pull/7870
*/
allowDefaultProjectFallbackFilesGlobs?: string[];

/**
* ***EXPERIMENTAL FLAG*** - Use this at your own risk.
*
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,15 +10,12 @@ export function useProgramFromProjectService(
projectService: server.ProjectService,
parseSettings: Readonly<MutableParseSettings>,
): ASTAndDefiniteProgram | undefined {
const opened =projectService.openClientFile(
projectService.openClientFile(
absolutify(parseSettings.filePath),
parseSettings.codeFullText,
/* scriptKind */ undefined,
parseSettings.tsconfigRootDir,
);
if (!opened.configFileName) {
return undefined;
}

const scriptInfo = projectService.getScriptInfo(parseSettings.filePath);
const program = projectService
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp