Commit 956dcc3

Anton Golub <antongolub@antongolub.com>
2023-08-18 00:36:08
fix(deps): relax pkg name pattern to comply with npm pkg name validation (#663)
* fix: relax pkg name pattern closes #659 closes #660 * test: check scoped pkg entry parsing with v declaration * fix(deps): parse pre-relelease versions
1 parent b1ca232
Changed files (2)
src/deps.ts
@@ -94,8 +94,10 @@ const importRe = [
   /\brequire\(['"](?<path>[^'"]+)['"]\)/,
   /\bfrom\s+['"](?<path>[^'"]+)['"]/,
 ]
-const nameRe = /^(?<name>(@[a-z0-9-]+\/)?[a-z0-9-.]+)\/?.*$/i
-const versionRe = /(\/\/|\/\*)\s*@(?<version>[~^]?([\dvx*]+([-.][\dx*]+)*))/i
+const nameRe =
+  /^(?<name>(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*)\/?.*$/i
+const versionRe =
+  /(\/\/|\/\*)\s*@(?<version>[~^]?(v?[\dx*]+([-.][\d*a-z-]+)*))/i
 
 export function parseDeps(content: Buffer): Record<string, string> {
   const deps: Record<string, string> = {}
test/deps.test.js
@@ -37,11 +37,35 @@ test('installDeps() loader works via CLI', async () => {
 })
 
 test('parseDeps(): import or require', async () => {
-  assert.equal(parseDeps(`import "foo"`), { foo: 'latest' })
-  assert.equal(parseDeps(`import * as bar from "foo"`), { foo: 'latest' })
-  assert.equal(parseDeps(`import('foo')`), { foo: 'latest' })
-  assert.equal(parseDeps(`require('foo')`), { foo: 'latest' })
-  assert.equal(parseDeps(`require('foo.js')`), { 'foo.js': 'latest' })
+  ;[
+    [`import "foo"`, { foo: 'latest' }],
+    [`import "foo"`, { foo: 'latest' }],
+    [`import * as bar from "foo"`, { foo: 'latest' }],
+    [`import('foo')`, { foo: 'latest' }],
+    [`require('foo')`, { foo: 'latest' }],
+    [`require('foo/bar')`, { foo: 'latest' }],
+    [`require('foo/bar.js')`, { foo: 'latest' }],
+    [`require('foo-bar')`, { 'foo-bar': 'latest' }],
+    [`require('foo_bar')`, { foo_bar: 'latest' }],
+    [`require('@foo/bar')`, { '@foo/bar': 'latest' }],
+    [`require('@foo/bar/baz')`, { '@foo/bar': 'latest' }],
+    [`require('foo.js')`, { 'foo.js': 'latest' }],
+
+    // ignores local deps
+    [`import '.'`, {}],
+    [`require('.')`, {}],
+    [`require('..')`, {}],
+    [`require('../foo.js')`, {}],
+    [`require('./foo.js')`, {}],
+
+    // ignores invalid pkg names
+    [`require('_foo')`, {}],
+    [`require('@')`, {}],
+    [`require('@/_foo')`, {}],
+    [`require('@foo')`, {}],
+  ].forEach(([input, result]) => {
+    assert.equal(parseDeps(input), result)
+  })
 })
 
 test('parseDeps(): import with org and filename', async () => {
@@ -74,6 +98,8 @@ test('parseDeps(): multiline', () => {
   import foo from "foo"
   import bar from "bar" /* @1.0.0 */
   import baz from "baz" //    @^2.0
+  import qux from "@qux/pkg/entry" //    @^3.0
+  import {api as alias} from "qux/entry/index.js" // @^4.0.0-beta.0
 
   const cpy = await import('cpy')
   const { pick } = require("lodash") //  @4.17.15
@@ -93,6 +119,8 @@ test('parseDeps(): multiline', () => {
     foo: 'latest',
     bar: '1.0.0',
     baz: '^2.0',
+    '@qux/pkg': '^3.0',
+    qux: '^4.0.0-beta.0',
     cpy: 'latest',
     lodash: '4.17.15',
   })