template-kit/license-denied
The template-kit/license-denied authoring rule — what it enforces, why, and how to fix it.
Every package in your production tree must carry a license from a fixed allowlist. There is no per-package override.
Each package in the production dependency tree must declare a license on this list:
| Allowed |
|---|
MIT |
ISC |
Apache-2.0 |
BSD-2-Clause |
BSD-3-Clause |
0BSD |
Unlicense |
CC0-1.0 |
Two cases people expect to slide through, and neither does:
SEE LICENSE IN <file>is a denial, not a pass. The check cannot read the file, so it cannot clear the terms.- A missing
licensefield is a denial, not a pass. An unlicensed package is not a permissive one — by default, no rights are granted at all.
Scope: the production tree only. “Production tree” means exactly what
npm ci --omit=dev installs. devDependencies are not evaluated — your bundler, your
test runner, and your formatter do not ship to anyone.
An optional peer you have anyway counts. If a production package declares an
optional peerDependency and your workspace has that package for any reason (even as a
devDependency), npm resolves the peer and keeps it in the --omit=dev install — so it
is in the production tree and its license is checked, transitively. An optional peer you
do not have is never installed and is not evaluated. Run npm ci --omit=dev and look
at the resulting node_modules if you want the exact set the check sees.
There is no per-package override. No allowlist entry you can add, no waiver comment. By design.
Reason
Section titled “Reason”Your template’s production dependencies are compiled into a page we host, and serve, on behalf of a paying customer. Their license terms become terms we are shipping under. A license we cannot clear in advance is a license we cannot ship — so the check is a gate rather than a warning, and the list is the set we have already cleared.
Replace the package with an equivalent whose license is on the list. That is the only fix, and for the overwhelming majority of what a section needs there is an MIT or Apache-2.0 alternative.
One legitimate adjacent fix: if the package is genuinely build-time only — it never
reaches the published bundle — then it belongs in devDependencies anyway, and
devDependencies are not evaluated. Be honest about this. If your published page imports
it, it is a production dependency no matter which stanza it is listed under, and moving it
is a bug in your package.json rather than a fix.
Before
Section titled “Before”{ "dependencies": { "react": "^19.0.0", "react-dom": "^19.0.0", "fancy-charts": "^3.2.0" // "license": "GPL-3.0-only" → denied }, "devDependencies": { "typescript": "^5.7.2" }}{ "dependencies": { "react": "^19.0.0", "react-dom": "^19.0.0", "lightweight-charts": "^4.2.0" // "license": "Apache-2.0" → allowed }, "devDependencies": { "typescript": "^5.7.2" }}npm uninstall fancy-chartsnpm install lightweight-chartsConfirm what a package declares before you commit to it:
npm view <package> licenseSee also
Section titled “See also”audit-severity— the other gate on the packages you ship.size-renderer-bundle— the third: what those packages cost the page in bytes.