core-js and Babel helpers: legacy JavaScript in a bundle
Polyfill bundles and ES5 transpilation helpers shipped to browsers that do not need them. squirrel reads script URLs and inline code.
What legacy JavaScript is
A polyfill implements a feature the browser lacks. A transpiler rewrites modern syntax into older syntax that more browsers understand. Both were necessary when a meaningful share of traffic ran browsers without Promise or arrow functions. That share is now small, and the code is still shipped by default in many build configurations.
The cost is bytes and parse time for every visitor, including the majority whose browser needed none of it. The lever is the build target: a browserslist that names modern browsers makes the transpiler and the polyfill injector emit far less.
What squirrel checks
The rule runs on every crawled page and looks at three things. It emits one to three checks:
legacy-polyfills: warns with the messageN legacy polyfill(s) detected, listing each by name. It matches eleven patterns in<script src>URLs:core-js,regenerator-runtime,babel-polyfill,polyfill.io,es5-shim,es6-shim,promise-polyfill,fetch-polyfill,whatwg-fetch,@babel/runtimeandtslib.es5-code-patterns: reports at info status with the messageN ES5 transpilation pattern(s) found, listing up to five. It scans inline scripts of 500 characters or more, skipping any whose type mentionsjson, for nine signatures including_classCallCheck,_createClass,_inherits,_typeof,__esModule,__spreadArray,__awaiterand__generator.differential-serving: passes with the messageUses module/nomodule differential servingwhen the page has both ascript[type="module"]and anomodulescript.es-modules: passes with the messageN ES module script(s) detectedwhen there are module scripts but nonomodulecounterpart.legacy-js: reports at info status with the messageNo legacy JavaScript patterns detectedwhen there are no polyfills, no ES5 patterns and no module scripts.
Every result here is a warning at most, and the rule’s severity is info. A page with no parsed document emits nothing at all.
Elements inside <noscript> are skipped: a browser with scripting enabled reads <noscript> content as plain text, so nothing in it loads or renders. See content inside noscript.
How to fix it
Set the build target to the browsers the site supports:
{
"browserslist": ["> 0.5%", "last 2 versions", "not dead"]
}Babel and the bundlers read that list, and a modern target removes most of the helpers and nearly all of the polyfills. TypeScript has its own target compiler option, which controls the same thing for its helpers. Where a specific feature still needs one, importing that polyfill alone is far smaller than a whole core-js bundle.
The common false positive is tslib. It is TypeScript’s helper library rather than a polyfill, and importing it is usually the smaller option, because the alternative is inlining the same helpers into every file that needs them.
| Rule ID | perf/legacy-js |
| Category | Performance |
| Scope | Per-page |
| Severity | info |
| Weight | 3/10 |
Enable / disable
Disable this rule
[rules]
disable = ["perf/legacy-js"]Disable all Performance rules
[rules]
disable = ["perf/*"]Enable only this rule
[rules]
enable = ["perf/legacy-js"]enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.
Related rules
- perf/js-file-size: the size those polyfills add to.
- perf/duplicate-js: the same library shipped more than once.
- perf/unminified-js: the other sign of a build that did not run in production mode.
- perf/js-libraries: which libraries the page loads and at what version.
Performance findings ship in every audit next to the SEO, security and agent experience rules. See Core Web Vitals for how an agent works through a report.
References
Check your site
Run squirrel audit https://example.com and open the Performance section of the report. Each polyfill and transpilation pattern is listed by name. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.