Commit 6f7b331

Anton Medvedev <anton@medv.io>
2022-09-25 11:54:10
Improve deps parsing
1 parent b684619
Changed files (2)
src/deps.ts
@@ -28,8 +28,6 @@ export async function installDeps(
   await $`npm install --no-save --no-audit --no-fund ${flags} ${packages}`
 }
 
-// https://github.com/nodejs/node/blob/5fad0b93667ffc6e4def52996b9529ac99b26319/test/parallel/test-internal-module-require.js
-// + wasi https://nodejs.org/api/wasi.html
 const builtins = new Set([
   '_http_agent',
   '_http_client',
@@ -87,18 +85,36 @@ const builtins = new Set([
   'worker_threads',
   'zlib',
 ])
+const importRe = [
+  /\bimport\s+['"](?<path>[^'"]+)['"]/,
+  /\bimport\(['"](?<path>[^'"]+)['"]\)/,
+  /\brequire\(['"](?<path>[^'"]+)['"]\)/,
+  /\bfrom\s+['"](?<path>[^'"]+)['"]/,
+]
+const nameRe = /^(?<name>(@[a-z0-9-]+\/)?[a-z0-9-]+)\/?.*$/i
+const versionRe = /(\/\/|\/\*)\s*@(?<version>[~^]?([\dvx*]+([-.][\dx*]+)*))/i
 
 export function parseDeps(content: Buffer): Record<string, string> {
-  const re =
-    /(?:\sfrom\s+|(?:[\s(\[{:;=+\-*|/~^%&,]|\.{3}|^)(?:import\s*\(?|require\s*\())["']((?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*)[/a-z0-9-._~]*["'](?:\)?[\s;,]*(?:\/\*|\/\/)\s*([a-z0-9-._~^*]+))?/g
   const deps: Record<string, string> = {}
-
-  let m
-  do {
-    m = re.exec(content.toString())
-    if (m && !builtins.has(m[1])) {
-      deps[m[1]] = m[2] || 'latest'
+  const lines = content.toString().split('\n')
+  for (let line of lines) {
+    for (let re of importRe) {
+      const m1 = re.exec(line)
+      if (m1 && m1.groups) {
+        const m2 = nameRe.exec(m1.groups.path)
+        if (m2 && m2.groups) {
+          const name = m2.groups.name
+          if (!builtins.has(name)) {
+            let version = 'latest'
+            const m3 = versionRe.exec(line)
+            if (m3 && m3.groups) {
+              version = m3.groups.version
+            }
+            deps[name] = version
+          }
+        }
+      }
     }
-  } while (m)
+  }
   return deps
 }
test/deps.test.js
@@ -32,28 +32,50 @@ test('installDeps() loader works via JS API', async () => {
 
 test('installDeps() loader works via CLI', async () => {
   let out =
-    await $`npm_config_registry="https://registry.yarnpkg.com" node build/cli.js --install <<< 'import _ from "lodash" /* 4.17.15 */; console.log(_.VERSION)'`
+    await $`node build/cli.js --install <<< 'import _ from "lodash" /* @4.17.15 */; console.log(_.VERSION)'`
   assert.match(out.stdout, '4.17.15')
 })
 
-test('parseDeps() extracts deps map', () => {
-  const contents = `require('a') // 1.0.0
-  const b =require('b') /* 2.0.0 */
-  const c = {c:require('c') /* 3.0.0 */, d: await import('d') /* 4.0.0 */, ...require('e') /* 5.0.0 */}
-  const f = [...require('f') /* 6.0.0 */] 
-  ;require('g'); // 7.0.0
-  const h = 1 *require('h') // 8.0.0
-  {require('i') /* 9.0.0 */}
-  import 'j' // 10.0.0
+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' })
+})
+
+test('parseDeps(): import with org and filename', async () => {
+  assert.equal(parseDeps(`import "@foo/bar/file"`), { '@foo/bar': 'latest' })
+})
+
+test('parseDeps(): import with version', async () => {
+  assert.equal(parseDeps(`import "foo" // @2.x`), { foo: '2.x' })
+  assert.equal(parseDeps(`import "foo" // @^7`), { foo: '^7' })
+  assert.equal(parseDeps(`import "foo" /* @1.2.x */`), { foo: '1.2.x' })
+})
+
+test('parseDeps(): multiline', () => {
+  const contents = `
+  require('a') // @1.0.0
+  const b =require('b') /* @2.0.0 */
+  const c = {
+    c:require('c') /* @3.0.0 */, 
+    d: await import('d') /* @4.0.0 */, 
+    ...require('e') /* @5.0.0 */
+  }
+  const f = [...require('f') /* @6.0.0 */] 
+  ;require('g'); // @7.0.0
+  const h = 1 *require('h') // @8.0.0
+  {require('i') /* @9.0.0 */}
+  import 'j' // @10.0.0
 
   import fs from 'fs'
   import path from 'path'
   import foo from "foo"
-  import bar from "bar" /* 1.0.0 */
-  import baz from "baz" //    ^2.0
+  import bar from "bar" /* @1.0.0 */
+  import baz from "baz" //    @^2.0
 
   const cpy = await import('cpy')
-  const { pick } = require("lodash") //  4.17.15
+  const { pick } = require("lodash") //  @4.17.15
   `
 
   assert.equal(parseDeps(contents), {