Add the ability to use local optimization_config#276
Conversation
…ization' To ensure consistency with other names. Most variable and function names in that file start with 'optimization' without the 'filter' prefix.
|
commit singing missed 😅 |
a69c0f0 to
e54e49d
Compare
Add unit tests covering `optimizationConfigLocal` and its members.
Introduce `optimizationConfigLocal` namespace with three members:
- `setPath(configPath)`: Sets the local config directory path. When set,
`getOptimizationPercent` and `getOptimizationConfig` read from local
files instead of downloading from remote URLs.
- `generate(configPath)`: Downloads `percent.json` and per-filter
`stats.json` from remote and saves them to `configPath`, creating
`configPath/percent.json` and `configPath/filters/{filterId}/stats.json`.
Per-filter file writes run in parallel via `Promise.all`.
- `reset()`: Clears the local config path and nulls the in-memory
`optimizationPercent` cache. Intended for restoring clean state between
tests.
GitHub shorthand installation does not trigger a build step, so dist/ must be present in the branch directly. This is a temporary measure for the PR review period. dist/ should be removed from the branch before merging, and the version should be published to npm instead.
e54e49d to
615d6e0
Compare
…onStats "Config" was ambiguous — the function returns per-filter stats (hit counts, groups), not configuration. "Stats" matches the source data (stats.json) and the new generateStats() method name.
…izationConfig
BREAKING CHANGE: exported name changed. Update all import sites:
- before: import { optimizationConfigLocal } from '@adguard/filters-compiler'
- after: import { localOptimizationConfig } from '@adguard/filters-compiler'
Noun-first naming matches the other module-level vars
(localOptimizationConfigPath, optimizationPercent).
download percent.json locally first, enabling users to edit it Before stats are fetched. downloadStatsFromPercentJson() fills in missing stats via two-steps lookup: local file → network download. Local stats.json files are preserved (never overwritten), so user edits to stats directly affect the compiled output. getOptimizationConfig() reads from in-memory cache (populated by downloadStatsFromPercentJson()) when localOptimizationConfigPath is set, instead of re-reading the file on every call.
reset() now takes an explicit configPath instead of reading stored state. getOptimizationStats() simplified: cache hit → return, else download. BREAKING CHANGE: setPath(), getOptimizationPercent() no longer exported; reset() signature changed from reset() to reset(configPath).
I will perform a force-push to exclude the changes in the |
238440f to
3e24d93
Compare
maximtop
left a comment
There was a problem hiding this comment.
I also tested the compiled package with optimization enabled. Platform generation fails on a non-optimized filter because the new code requests filters/10/stats.json and the server returns 404. Existing builder tests do not catch this because they call disableOptimization().
| describe('local optimization config', () => { | ||
| let tmpDir; | ||
|
|
||
| afterEach(() => { |
There was a problem hiding this comment.
reset is async but this afterEach does not await or return it. Also, the nested test block declares another const tmpDir, so cleanup still uses the outer tmpDir and leaves the stats test directory behind.
Please make cleanup awaited and avoid shadowing tmpDir.
There was a problem hiding this comment.
I applied your comment. Please see the changes.
By the way, should I keep using fs.promises in 'reset()` or switch to synchronous methods?
I mean that the others, like readFile, writeFile, and mkdtemp, are called either synchronously or asynchronously in the existing codebase.
In short: When should I call the node fs module synchronously or asynchronously in this project?
There was a problem hiding this comment.
In this PR, I switched to using fs.promises today for consistency (d393a90) because I had been using both.
There was a problem hiding this comment.
I applied your comment. Please see the changes.
By the way, should I keep using
fs.promisesin 'reset()` or switch to synchronous methods?I mean that the others, like
readFile,writeFile, andmkdtemp, are called either synchronously or asynchronously in the existing codebase.In short: When should I call the node
fsmodule synchronously or asynchronously in this project?
For this PR, fs.promises is the right direction. These helpers are already async and can run during build workflows, so async filesystem calls avoid blocking the event loop and keep the API consistent.
Sync fs is fine for tiny startup/config reads or test setup when it makes tests simpler, but new runtime/build-path code should generally prefer fs.promises.
Without the gate, every uncached filterId called
filters/{id}/stats.json, which 404s for non-optimized filters.
- Re-introduce optimizableFilterIds Set (lazily loaded from
percent.json on first getOptimizationStats call)
- Return null for filterIds not listed in percent.json
- Restore groups array validation on downloaded stats
- Populate optimizableFilterIds in downloadStatsFromPercentJson
- Reset optimizableFilterIds to null in reset()
- Extract PERCENT_JSON / STATS_JSON / FILTERS_DIR constants
- Add JSDoc to localOptimizationConfig and getOptimizationStats
- Move async setup from describe callbacks into beforeAll/afterAll - Remove inner const tmpDir shadowing the outer let tmpDir - Await localOptimizationConfig.reset() in cleanup hooks - Add: getOptimizationStats returns null for unlisted filterId and does not call the stats endpoint
It currently works synchronously, but the code becomes misleading and unsafe for future refactoring.
There was a problem hiding this comment.
I wrapped the download functions with async for consistency, because some parts were already called with await, and fixed the unsafe for future refactoring.
0b51ccb#diff-4a18d6d902a0c695e320cad11d7803a4d99d655e107051d5f5794a2c46509704R94
maximtop
left a comment
There was a problem hiding this comment.
One blocking repository-level issue remains: this PR is currently not mergeable with the current master. GitHub reports mergeable_state=dirty; the PR is still based on 6e0fa5a (v3.2.6), while current master is b75b738. Please rebase or merge master before final review, because package / Rollup / changelog areas have changed on master since this PR branched off.
| // config: [{filterId: 1, percent: 45}, ...] | ||
| const filterOptimizationPercent = getFiltersOptimizationPercent().config | ||
| .find((config) => config.filterId === filterId); | ||
| if (optimizationStatsCache[filterId] !== undefined) { |
There was a problem hiding this comment.
Cached local stats bypass the validation below. downloadStatsFromPercentJson() stores parsed local stats.json directly in optimizationStatsCache, and this early return sends it to callers without checking that groups exists and is non-empty.
Please apply the same validation to cached and downloaded stats, preferably via a small shared helper.
|
|
||
| - `localOptimizationConfig` export for managing a local optimization config | ||
| cache: download `percent.json`, pre-fetch per-filter `stats.json`, and reset | ||
| cached state. Intended for offline / reproducible local filter builds. |
There was a problem hiding this comment.
Project rules require an [Unreleased] link reference when adding the ## [Unreleased] section. Please add it after the section entries, e.g.
[Unreleased]: https://github.com/AdguardTeam/FiltersCompiler/compare/v3.2.6...HEAD
| logPath: string, | ||
| reportPath: string, | ||
| platformsPath: string, | ||
| whitelist: number[], |
There was a problem hiding this comment.
These typings are stricter than the actual JS API. compile() callers can omit whitelist / blacklist and this code also accepts null, but the declaration requires number[].
Please make these arguments optional and nullable to match runtime usage, and do the same for customPlatformsConfig if null is expected.
Related: AdguardTeam/FiltersRegistry#1186
Summary
filterOptimization*→optimization*for consistency with existing naming inoptimization.jslocalOptimizationConfignamespace allows loadingpercent.jsonand per-filterstats.jsonfrom a local directory instead of fetching from remote URLssetPath(configPath), sets the local config dir; once set, getOptimizationPercent / getOptimizationConfig read locallydownloadPercentJson(configPath)anddownloadStatsFromPercentJson(configPath), downloads thepercent.jsonandstats.jsonfor each filter to the local dir for future use; per-filter writes run in parallelreset()— clears path and in-memory cachedist/: Built artifacts included so the package can be installed via GitHub shorthand URL (e.g. npm iAdguardTeam/FiltersCompiler#branch)Test plan
localOptimizationConfig(setPath,downloadPercentJson,downloadStatsFromPercentJsonreset) passdist/artifacts match npm run build output