Commit b684619

Anton Golub <antongolub@antongolub.com>
2022-09-18 21:53:42
fix: handle more require/import api use cases on parsing (#514)
* style: use const for immutable vars, rm redundant awaits * fix: handle more require/import api use cases * fix: minor impr
1 parent 96d40f9
src/cli.ts
@@ -97,14 +97,9 @@ await (async function main() {
     await scriptFromHttp(firstArg)
     return
   }
-  let filepath
-  if (firstArg.startsWith('/')) {
-    filepath = firstArg
-  } else if (firstArg.startsWith('file:///')) {
-    filepath = url.fileURLToPath(firstArg)
-  } else {
-    filepath = resolve(firstArg)
-  }
+  const filepath = firstArg.startsWith('file:///')
+    ? url.fileURLToPath(firstArg)
+    : resolve(firstArg)
   await importPath(filepath)
 })().catch((err) => {
   if (err instanceof ProcessOutput) {
@@ -164,21 +159,21 @@ async function writeAndImport(
 }
 
 async function importPath(filepath: string, origin = filepath) {
-  let ext = extname(filepath)
+  const ext = extname(filepath)
 
   if (ext === '') {
-    let tmpFilename = fs.existsSync(`${filepath}.mjs`)
+    const tmpFilename = fs.existsSync(`${filepath}.mjs`)
       ? `${basename(filepath)}-${randomId()}.mjs`
       : `${basename(filepath)}.mjs`
 
-    return await writeAndImport(
+    return writeAndImport(
       await fs.readFile(filepath),
       join(dirname(filepath), tmpFilename),
       origin
     )
   }
   if (ext === '.md') {
-    return await writeAndImport(
+    return writeAndImport(
       transformMarkdown(await fs.readFile(filepath)),
       join(dirname(filepath), basename(filepath) + '.mjs'),
       origin
@@ -189,16 +184,16 @@ async function importPath(filepath: string, origin = filepath) {
     console.log('Installing dependencies...', deps)
     await installDeps(deps, dirname(filepath))
   }
-  let __filename = resolve(origin)
-  let __dirname = dirname(__filename)
-  let require = createRequire(origin)
+  const __filename = resolve(origin)
+  const __dirname = dirname(__filename)
+  const require = createRequire(origin)
   Object.assign(global, { __filename, __dirname, require })
   await import(url.pathToFileURL(filepath).toString())
 }
 
 function transformMarkdown(buf: Buffer) {
-  let source = buf.toString()
-  let output = []
+  const source = buf.toString()
+  const output = []
   let state = 'root'
   let prevLineIsEmpty = true
   for (let line of source.split('\n')) {
src/core.ts
@@ -91,7 +91,7 @@ export const $ = new Proxy<Shell & Options>(
       throw new Error(`Malformed command at ${from}`)
     }
     let resolve: Resolve, reject: Resolve
-    let promise = new ProcessPromise((...args) => ([resolve, reject] = args))
+    const promise = new ProcessPromise((...args) => ([resolve, reject] = args))
     let cmd = pieces[0],
       i = 0
     while (i < args.length) {
src/deps.ts
@@ -28,17 +28,75 @@ export async function installDeps(
   await $`npm install --no-save --no-audit --no-fund ${flags} ${packages}`
 }
 
-const builtinsRe =
-  /^(_http_agent|_http_client|_http_common|_http_incoming|_http_outgoing|_http_server|_stream_duplex|_stream_passthrough|_stream_readable|_stream_transform|_stream_wrap|_stream_writable|_tls_common|_tls_wrap|assert|async_hooks|buffer|child_process|cluster|console|constants|crypto|dgram|diagnostics_channel|dns|domain|events|fs|http|http2|https|inspector|module|net|os|path|perf_hooks|process|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|trace_events|tty|url|util|v8|vm|wasi|worker_threads|zlib)$/
+// 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',
+  '_http_common',
+  '_http_incoming',
+  '_http_outgoing',
+  '_http_server',
+  '_stream_duplex',
+  '_stream_passthrough',
+  '_stream_readable',
+  '_stream_transform',
+  '_stream_wrap',
+  '_stream_writable',
+  '_tls_common',
+  '_tls_wrap',
+  'assert',
+  'async_hooks',
+  'buffer',
+  'child_process',
+  'cluster',
+  'console',
+  'constants',
+  'crypto',
+  'dgram',
+  'dns',
+  'domain',
+  'events',
+  'fs',
+  'http',
+  'http2',
+  'https',
+  'inspector',
+  'module',
+  'net',
+  'os',
+  'path',
+  'perf_hooks',
+  'process',
+  'punycode',
+  'querystring',
+  'readline',
+  'repl',
+  'stream',
+  'string_decoder',
+  'sys',
+  'timers',
+  'tls',
+  'trace_events',
+  'tty',
+  'url',
+  'util',
+  'v8',
+  'vm',
+  'wasi',
+  'worker_threads',
+  'zlib',
+])
 
 export function parseDeps(content: Buffer): Record<string, string> {
   const re =
-    /(?:\sfrom\s+|[\s(:\[](?:import|require)\s*\()["']((?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*)[/a-z0-9-._~]*["'](?:\s*;?\s*(?:\/\*|\/\/)\s*([a-z0-9-._~^*]+))?/g
+    /(?:\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 && !builtinsRe.test(m[1])) {
+    if (m && !builtins.has(m[1])) {
       deps[m[1]] = m[2] || 'latest'
     }
   } while (m)
src/goods.ts
@@ -55,7 +55,7 @@ export async function fetch(url: RequestInfo, init?: RequestInit) {
 export function echo(...args: any[]): void
 export function echo(pieces: TemplateStringsArray, ...args: any[]) {
   let msg
-  let lastIdx = pieces.length - 1
+  const lastIdx = pieces.length - 1
   if (
     Array.isArray(pieces) &&
     pieces.every(isString) &&
test/deps.test.js
@@ -37,7 +37,15 @@ test('installDeps() loader works via CLI', async () => {
 })
 
 test('parseDeps() extracts deps map', () => {
-  const contents = `
+  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"
@@ -45,15 +53,25 @@ test('parseDeps() extracts deps map', () => {
   import baz from "baz" //    ^2.0
 
   const cpy = await import('cpy')
-  const { pick } = require('lodash')
+  const { pick } = require("lodash") //  4.17.15
   `
 
   assert.equal(parseDeps(contents), {
+    a: '1.0.0',
+    b: '2.0.0',
+    c: '3.0.0',
+    d: '4.0.0',
+    e: '5.0.0',
+    f: '6.0.0',
+    g: '7.0.0',
+    h: '8.0.0',
+    i: '9.0.0',
+    j: '10.0.0',
     foo: 'latest',
     bar: '1.0.0',
     baz: '^2.0',
     cpy: 'latest',
-    lodash: 'latest',
+    lodash: '4.17.15',
   })
 })