Regex npm package name
An npm package name isn't just "letters, digits and hyphens": npm actually validates the name through a chain of imperative checks (blocklist, max length, URL encoding), never a single shape regex. The well-followed scoped-regex library, published by a well-known author, enables the case-insensitive flag - which wrongly "validates" an uppercase scope, even though npm strictly forbids them. Here's a pattern respecting that constraint, compared against that library and two other attempts, each missing a real rule from the official implementation.
/^(?:@[a-z0-9~][a-z0-9-._~]*/)?[a-z0-9~][a-z0-9-._~]*$/passes 20/20 cases
Comparison of existing patterns
| Source | pattern | Score on the corpus | ReDoS |
|---|---|---|---|
| sindresorhus/scoped-regex (paquet npm réel et populaire) — utilisé avec le flag insensible à la casse, « valide » à tort un scope en majuscules pourtant strictement interdit par npm | @[a-z\d][\w-.]+/[a-z\d][\w-.]* | 13 / 20 | safe |
| Gist GitHub (mhafemann) — accepte à tort un nom commençant par un tiret (seuls le point et l'underscore sont documentés comme interdits en tête dans cette regex précise), et ne vérifie ni la limite de 214 caractères, ni la liste noire (node_modules, favicon.ico), ni les modules cœur de Node | ^(@[a-z0-9-~][a-z0-9-._~]*/)?[a-z0-9-~][a-z0-9-._~]*$ | 19 / 20 | safe |
| Pattern interne de validate-npm-package-name (scopedPackagePattern), volontairement permissif — ne sert qu'à séparer @scope et nom, jamais à valider la forme complète d'un nom de paquet | ^(?:@([^/]+?)[/])?([^/]+?)$ | 10 / 20 | safe |
Implementation
const re = new RegExp("^(?:@[a-z0-9~][a-z0-9-._~]*/)?[a-z0-9~][a-z0-9-._~]*$", "");
re.test(input);Limits
- Valide seulement la FORME générale (minuscules, scope optionnel) : n'impose pas la limite officielle de 214 caractères (avertissement seulement dans l'implémentation réelle npm, pas un rejet), ni la liste noire de noms réservés (« node_modules », « favicon.ico »).
- N'exclut pas les noms de modules cœur de Node.js (« http », « fs », « path »…) : npm les rejette pour éviter toute confusion avec un import natif, une règle par liste explicite qu'une regex de forme ne peut pas exprimer.
- Ne vérifie pas que le nom reste sûr une fois encodé en URL (l'implémentation officielle compare le nom à `encodeURIComponent(name)`) : certains caractères spéciaux structurellement acceptés par ce motif peuvent malgré tout être rejetés par npm pour cette raison.
- npm valide en réalité un nom de paquet avec une suite de vérifications impératives, jamais une seule regex : cette page teste la partie « forme » de cette suite, pas son équivalent complet.