Skip to content

Commit 8535fc2

Browse files
authored
perf(fmt): reuse resolved options (#338)
1 parent 9873df3 commit 8535fc2

4 files changed

Lines changed: 62 additions & 11 deletions

File tree

packages/rstack/src/fmt/config.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,20 @@ type ResolveFmtConfigOptions = {
1717
type PathMatcher = (filePath: string) => boolean;
1818
type FmtOptionsResolver = (filePath: string) => ResolvedFmtOptions;
1919

20+
/**
21+
* Each path from the root represents an ordered sequence of matching overrides.
22+
* A node stores the options merged along that path.
23+
*/
24+
type OptionsCacheNode = {
25+
children: WeakMap<ResolvedFmtOptions, OptionsCacheNode>;
26+
options: ResolvedFmtOptions;
27+
};
28+
29+
const createOptionsCacheNode = (options: ResolvedFmtOptions): OptionsCacheNode => ({
30+
children: new WeakMap(),
31+
options,
32+
});
33+
2034
const neverMatches: PathMatcher = () => false;
2135

2236
const compileMatchers = (
@@ -96,22 +110,27 @@ const createOptionsResolver = (config: ResolvedFmtConfig): FmtOptionsResolver =>
96110
}
97111

98112
const resolveRelativePath = createRelativePathResolver(config.rootPath);
113+
const rootCacheNode = createOptionsCacheNode(config.baseOptions);
99114

100115
return (filePath) => {
101-
let options = config.baseOptions;
116+
let cacheNode = rootCacheNode;
102117
const relativeFilePath = resolveRelativePath(filePath);
103118

104119
for (const override of config.overrides) {
105120
if (!override.options || !override.matches(relativeFilePath)) {
106121
continue;
107122
}
108-
if (options === config.baseOptions) {
109-
options = { ...options };
123+
124+
// Reuse the merged result for this override after the current matched sequence.
125+
let nextCacheNode = cacheNode.children.get(override.options);
126+
if (!nextCacheNode) {
127+
nextCacheNode = createOptionsCacheNode({ ...cacheNode.options, ...override.options });
128+
cacheNode.children.set(override.options, nextCacheNode);
110129
}
111-
Object.assign(options, override.options);
130+
cacheNode = nextCacheNode;
112131
}
113132

114-
return options;
133+
return cacheNode.options;
115134
};
116135
};
117136

packages/rstack/src/fmt/plugins.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,12 @@ const createFingerprintResolver = (): FingerprintResolver => {
9494
/** Creates a project-root resolver for plugins in final per-file options. */
9595
const createPluginResolver = (rootPath: string): FmtPluginResolver => {
9696
const parentUrl = pathToFileURL(join(rootPath, 'index.js'));
97-
const cache = new Map<string, string>();
97+
const pluginCache = new Map<string, string>();
98+
const optionsCache = new WeakMap<ResolvedFmtOptions, ResolvedFmtOptions>();
9899

99100
const resolvePlugin = (plugin: FmtPluginSpecifier): string => {
100101
const specifier = plugin instanceof URL ? plugin.href : plugin;
101-
const cached = cache.get(specifier);
102+
const cached = pluginCache.get(specifier);
102103
if (cached !== undefined) {
103104
return cached;
104105
}
@@ -119,11 +120,16 @@ const createPluginResolver = (rootPath: string): FmtPluginResolver => {
119120
}
120121
}
121122

122-
cache.set(specifier, resolved);
123+
pluginCache.set(specifier, resolved);
123124
return resolved;
124125
};
125126

126127
return (options) => {
128+
const cached = optionsCache.get(options);
129+
if (cached !== undefined) {
130+
return cached;
131+
}
132+
127133
const { plugins } = options;
128134
if (!plugins?.length) {
129135
return options;
@@ -136,10 +142,11 @@ const createPluginResolver = (rootPath: string): FmtPluginResolver => {
136142
}
137143

138144
const resolvedPlugins = plugins.map(resolvePlugin);
139-
140-
return resolvedPlugins.every((plugin, index) => plugin === plugins[index])
145+
const resolvedOptions = resolvedPlugins.every((plugin, index) => plugin === plugins[index])
141146
? options
142147
: { ...options, plugins: resolvedPlugins };
148+
optionsCache.set(options, resolvedOptions);
149+
return resolvedOptions;
143150
};
144151
};
145152

packages/rstack/tests/fmt/config.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,29 @@ test('applies basename and path overrides in declaration order', () => {
5050
expect(config.baseOptions).toEqual({ singleQuote: false });
5151
});
5252

53+
test('reuses options for the same override combination', () => {
54+
const config = normalizeFmtConfig(
55+
{
56+
singleQuote: false,
57+
overrides: [
58+
{ files: '*.ts', options: { semi: false } },
59+
{ files: 'src/**/*.ts', options: { singleQuote: true } },
60+
],
61+
},
62+
rootPath,
63+
);
64+
const resolveOptions = createOptionsResolver(config);
65+
66+
const first = resolveOptions(path.join(rootPath, 'src/first.ts'));
67+
const second = resolveOptions(path.join(rootPath, 'src/second.ts'));
68+
const outside = resolveOptions(path.join(rootPath, 'outside.ts'));
69+
70+
expect(first).toBe(second);
71+
expect(first).not.toBe(outside);
72+
expect(first).toEqual({ semi: false, singleQuote: true });
73+
expect(outside).toEqual({ semi: false, singleQuote: false });
74+
});
75+
5376
test('applies overrides outside the config root', () => {
5477
const config = normalizeFmtConfig(
5578
{

packages/rstack/tests/fmt/plugins.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ test('resolves plugin specifiers from the config root', async () => {
4040
],
4141
};
4242

43-
const resolved = createPluginResolver(rootPath)(options);
43+
const resolvePlugins = createPluginResolver(rootPath);
44+
const resolved = resolvePlugins(options);
4445

4546
expect(resolved.plugins).toEqual([
4647
pathToFileURL(packageEntry).href,
@@ -50,6 +51,7 @@ test('resolves plugin specifiers from the config root', async () => {
5051
'data:text/javascript,export default {}',
5152
]);
5253
expect(options.plugins[0]).toBe('prettier-plugin-packagejson');
54+
expect(resolvePlugins(options)).toBe(resolved);
5355
});
5456
});
5557

0 commit comments

Comments
 (0)