Skip to content

Commit b6e8661

Browse files
authored
refactor(fmt): centralize per-file resolution (#334)
1 parent 98552d5 commit b6e8661

6 files changed

Lines changed: 97 additions & 118 deletions

File tree

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,9 @@
11
import path from 'node:path';
2-
import { createOptionsResolver, type FmtOptionsResolver } from './config.ts';
32
import { discoverFmtPaths } from './discoverPaths.ts';
3+
import { createFmtFileResolver } from './fileResolver.ts';
44
import { createIgnoreMatcher } from './ignore.ts';
5-
import type { FmtPluginResolver } from './plugins.ts';
65
import type { DiscoverFmtFilesOptions, FmtFileRequest } from './types.ts';
76

8-
const createFileRequest = (
9-
filePath: string,
10-
resolveOptions: FmtOptionsResolver,
11-
): FmtFileRequest => ({
12-
path: filePath,
13-
options: resolveOptions(filePath),
14-
});
15-
16-
/** Imports the plugin chunk on first use and shares the resolver across calls. */
17-
const createLazyPluginResolver = (rootPath: string): (() => Promise<FmtPluginResolver>) => {
18-
let resolver: Promise<FmtPluginResolver> | undefined;
19-
20-
return () =>
21-
(resolver ??= import(
22-
/* rspackChunkName: 'fmtPlugins' */
23-
'./plugins.ts'
24-
).then(({ createPluginResolver }) => createPluginResolver(rootPath)));
25-
};
26-
27-
/** Resolves the plugin specifiers of a request whose options configure plugins. */
28-
const resolveFileRequestPlugins = async (
29-
file: FmtFileRequest,
30-
getPluginResolver: () => Promise<FmtPluginResolver>,
31-
): Promise<FmtFileRequest> =>
32-
file.options.plugins?.length
33-
? { ...file, options: (await getPluginResolver())(file.options) }
34-
: file;
35-
367
const createDirMatcher = (dirPath: string): ((filePath: string) => boolean) => {
378
const prefix = dirPath.endsWith(path.sep) ? dirPath : `${dirPath}${path.sep}`;
389
return (filePath) => filePath === dirPath || filePath.startsWith(prefix);
@@ -63,14 +34,9 @@ const discoverFmtFiles = async ({
6334
return [];
6435
}
6536

66-
const resolveOptions = createOptionsResolver(config);
67-
const getPluginResolver = createLazyPluginResolver(config.rootPath);
37+
const resolveFile = createFmtFileResolver(config);
6838

69-
return Promise.all(
70-
filePaths.map((filePath) =>
71-
resolveFileRequestPlugins(createFileRequest(filePath, resolveOptions), getPluginResolver),
72-
),
73-
);
39+
return Promise.all(filePaths.map((filePath) => resolveFile(filePath)));
7440
};
7541

76-
export { createFileRequest, createLazyPluginResolver, discoverFmtFiles, resolveFileRequestPlugins };
42+
export { discoverFmtFiles };
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { createOptionsResolver } from './config.ts';
2+
import type { FmtPluginResolver } from './plugins.ts';
3+
import type { FmtFileRequest, ResolvedFmtConfig } from './types.ts';
4+
5+
type FmtFileResolver = (filePath: string) => Promise<FmtFileRequest>;
6+
7+
/** Applies per-file overrides and resolves configured plugin specifiers. */
8+
const createFmtFileResolver = (config: ResolvedFmtConfig): FmtFileResolver => {
9+
const resolveOptions = createOptionsResolver(config);
10+
let pluginResolver: Promise<FmtPluginResolver> | undefined;
11+
12+
return async (filePath) => {
13+
let options = resolveOptions(filePath);
14+
15+
if (options.plugins?.length) {
16+
pluginResolver ??= import(
17+
/* rspackChunkName: 'fmtPlugins' */
18+
'./plugins.ts'
19+
).then(({ createPluginResolver }) => createPluginResolver(config.rootPath));
20+
options = (await pluginResolver)(options);
21+
}
22+
23+
return { path: filePath, options };
24+
};
25+
};
26+
27+
export { createFmtFileResolver };
28+
export type { FmtFileResolver };

‎packages/rstack/src/fmt/lsp/server.ts‎

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,9 @@ import {
99
type InitializeParams,
1010
type TextEdit,
1111
} from 'vscode-languageserver/node';
12-
import { createOptionsResolver, type FmtOptionsResolver } from '../config.ts';
13-
import {
14-
createFileRequest,
15-
createLazyPluginResolver,
16-
resolveFileRequestPlugins,
17-
} from '../discovery.ts';
12+
import { createFmtFileResolver, type FmtFileResolver } from '../fileResolver.ts';
1813
import { formatFmtSource } from '../format.ts';
1914
import { createIgnoreMatcher, type IgnorePredicate } from '../ignore.ts';
20-
import type { FmtPluginResolver } from '../plugins.ts';
2115
import type { ResolvedFmtConfig } from '../types.ts';
2216
import { computeMinimalTextEdit } from './minimalEdit.ts';
2317

@@ -35,9 +29,7 @@ type FmtLspSessionOptions = RunFmtLspOptions & { root: string };
3529

3630
interface FmtLspSession {
3731
isIgnored: IgnorePredicate;
38-
resolveOptions: FmtOptionsResolver;
39-
/** Resolves plugin specifiers through the file system; cached per session. */
40-
getPluginResolver: () => Promise<FmtPluginResolver>;
32+
resolveFile: FmtFileResolver;
4133
}
4234

4335
const toFilePath = (uri: string): string | undefined => {
@@ -122,8 +114,7 @@ const createFmtLspSession = async ({
122114

123115
return {
124116
isIgnored,
125-
resolveOptions: createOptionsResolver(config),
126-
getPluginResolver: createLazyPluginResolver(config.rootPath),
117+
resolveFile: createFmtFileResolver(config),
127118
};
128119
};
129120

@@ -137,10 +128,7 @@ const formatDocumentSource = async (
137128
return undefined;
138129
}
139130

140-
const file = await resolveFileRequestPlugins(
141-
createFileRequest(filePath, session.resolveOptions),
142-
session.getPluginResolver,
143-
);
131+
const file = await session.resolveFile(filePath);
144132
const result = await formatFmtSource(file, () => source);
145133

146134
return result.status === 'formatted' ? result.formatted : undefined;

‎packages/rstack/src/fmt/stdin.ts‎

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import { resolve } from 'node:path';
2-
import { createOptionsResolver } from './config.ts';
3-
import {
4-
createFileRequest,
5-
createLazyPluginResolver,
6-
resolveFileRequestPlugins,
7-
} from './discovery.ts';
2+
import { createFmtFileResolver } from './fileResolver.ts';
83
import { formatFmtSource } from './format.ts';
94
import { createIgnoreMatcher } from './ignore.ts';
105
import type { ResolvedFmtConfig } from './types.ts';
@@ -83,10 +78,7 @@ const runFmtStdin = async ({
8378
return;
8479
}
8580

86-
const file = await resolveFileRequestPlugins(
87-
createFileRequest(absolutePath, createOptionsResolver(config)),
88-
createLazyPluginResolver(config.rootPath),
89-
);
81+
const file = await createFmtFileResolver(config)(absolutePath);
9082
const result = await formatFmtSource(file, () => source);
9183

9284
if (result.status === 'unsupported') {

‎packages/rstack/tests/fmt/discovery.test.ts‎

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { mkdirSync } from 'node:fs';
22
import path from 'node:path';
3-
import { pathToFileURL } from 'node:url';
43
import { expect, test } from 'rstack/test';
54
import { normalizeFmtConfig } from '../../src/fmt/config.ts';
65
import { discoverFmtFiles } from '../../src/fmt/discovery.ts';
@@ -128,56 +127,3 @@ test('defers parser inference to workers and preserves an explicit parser', asyn
128127
});
129128
});
130129
});
131-
132-
test('resolves plugins after applying matching overrides', async () => {
133-
await withTempProject(async (rootPath) => {
134-
const pluginEntry = writeProjectFile(
135-
rootPath,
136-
'node_modules/prettier-plugin-fixture/index.mjs',
137-
`export default {
138-
languages: [
139-
{ name: 'Fixture JSON', parsers: ['json'], extensions: ['.fixture'] },
140-
{ name: 'Fixture TypeScript', parsers: ['babel'], extensions: ['.ts'] },
141-
],
142-
};
143-
`,
144-
);
145-
writeProjectFile(
146-
rootPath,
147-
'node_modules/prettier-plugin-fixture/package.json',
148-
JSON.stringify({ name: 'prettier-plugin-fixture', exports: './index.mjs' }),
149-
);
150-
writeProjectFile(rootPath, 'example.fixture');
151-
writeProjectFile(rootPath, 'example.ts');
152-
const config = {
153-
overrides: [
154-
{
155-
files: '*.fixture',
156-
options: { plugins: ['prettier-plugin-fixture'] },
157-
},
158-
{
159-
files: '*.ts',
160-
options: { plugins: ['prettier-plugin-fixture'] },
161-
},
162-
{
163-
files: '*.md',
164-
options: { plugins: ['missing-plugin'] },
165-
},
166-
],
167-
};
168-
169-
const files = await discover(rootPath, ['example.fixture', 'example.ts'], config);
170-
171-
expect(files).toHaveLength(2);
172-
expect(files[0]).toMatchObject({
173-
options: {
174-
plugins: [pathToFileURL(pluginEntry).href],
175-
},
176-
});
177-
expect(files[1]).toMatchObject({
178-
options: {
179-
plugins: [pathToFileURL(pluginEntry).href],
180-
},
181-
});
182-
});
183-
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import path from 'node:path';
2+
import { pathToFileURL } from 'node:url';
3+
import { expect, test } from 'rstack/test';
4+
import { normalizeFmtConfig } from '../../src/fmt/config.ts';
5+
import { createFmtFileResolver } from '../../src/fmt/fileResolver.ts';
6+
import { withTempProject, writeProjectFile } from './helpers.ts';
7+
8+
test('applies matching overrides before resolving plugins', async () => {
9+
await withTempProject(async (rootPath) => {
10+
const pluginEntry = writeProjectFile(
11+
rootPath,
12+
'node_modules/prettier-plugin-fixture/index.mjs',
13+
`export default {
14+
languages: [
15+
{ name: 'Fixture JSON', parsers: ['json'], extensions: ['.fixture'] },
16+
{ name: 'Fixture TypeScript', parsers: ['babel'], extensions: ['.ts'] },
17+
],
18+
};
19+
`,
20+
);
21+
writeProjectFile(
22+
rootPath,
23+
'node_modules/prettier-plugin-fixture/package.json',
24+
JSON.stringify({ name: 'prettier-plugin-fixture', exports: './index.mjs' }),
25+
);
26+
const config = normalizeFmtConfig(
27+
{
28+
overrides: [
29+
{
30+
files: '*.{fixture,ts}',
31+
options: { plugins: ['prettier-plugin-fixture'] },
32+
},
33+
{
34+
files: '*.md',
35+
options: { plugins: ['missing-plugin'] },
36+
},
37+
],
38+
},
39+
rootPath,
40+
);
41+
const resolveFile = createFmtFileResolver(config);
42+
43+
const files = await Promise.all([
44+
resolveFile(path.join(rootPath, 'example.fixture')),
45+
resolveFile(path.join(rootPath, 'example.ts')),
46+
]);
47+
48+
expect(files).toEqual([
49+
{
50+
path: path.join(rootPath, 'example.fixture'),
51+
options: { plugins: [pathToFileURL(pluginEntry).href] },
52+
},
53+
{
54+
path: path.join(rootPath, 'example.ts'),
55+
options: { plugins: [pathToFileURL(pluginEntry).href] },
56+
},
57+
]);
58+
});
59+
});

0 commit comments

Comments
 (0)