@davidwells/extract-deps 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@davidwells/extract-deps",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Get imports and requires from a codebase without AST",
5
5
  "main": "src/index.js",
6
6
  "types": "types/index.d.ts",
@@ -1,6 +1,6 @@
1
1
  // Extracts CommonJS module.exports and exports patterns from JavaScript code
2
2
 
3
- const { getNames } = require('./utils')
3
+ const { getNames, extractExportName } = require('./utils')
4
4
 
5
5
  // Matches: module.exports = ... or exports = module.exports = ...
6
6
  // https://regex101.com/r/example/cjs-1
@@ -54,33 +54,6 @@ function getCjsExports(code) {
54
54
  return allExports.sort((a, b) => a.index - b.index)
55
55
  }
56
56
 
57
- function extractExportDefName(exportValue) {
58
- // Handle function declarations
59
- if (exportValue.includes('function') && !exportValue.includes('=>')) {
60
- const match = exportValue.match(/function\s+([\w$]+)/)
61
- return match ? match[1] : 'anonymous'
62
- }
63
-
64
- // Handle class declarations
65
- if (exportValue.includes('class')) {
66
- const match = exportValue.match(/class\s+([\w$]+)/)
67
- return match ? match[1] : 'anonymous'
68
- }
69
-
70
- // Handle arrow functions
71
- if (exportValue.includes('=>')) {
72
- return 'anonymous'
73
- }
74
-
75
- // Handle object/array literals
76
- if (exportValue.startsWith('{') || exportValue.startsWith('[')) {
77
- return 'literal'
78
- }
79
-
80
- // Handle simple values
81
- return exportValue.trim()
82
- }
83
-
84
57
  function extractCompleteValue(text, startIndex) {
85
58
  const firstChar = text[startIndex]
86
59
 
@@ -176,7 +149,7 @@ function getModuleExportsAssignment(originalText = '', sanitizedText = originalT
176
149
  }
177
150
  }
178
151
 
179
- const exportDefName = extractExportDefName(sanitizedCompleteValue)
152
+ const exportDefName = extractExportName(sanitizedCompleteValue)
180
153
 
181
154
  // Determine export type based on sanitized patterns
182
155
  let exportType = 'default'
@@ -203,7 +176,7 @@ function getModuleExportsAssignment(originalText = '', sanitizedText = originalT
203
176
  const definitions = getNames(sanitizedCompleteValue, 'definition')
204
177
  if (definitions) {
205
178
  exportStatement.definitions = definitions
206
- exportStatement.keys = definitions.destructuredParameters.map(param => param.name)
179
+ exportStatement.keys = definitions.destructuredParameters.map(param => param.name.replace(/,+$/, ''))
207
180
  }
208
181
  }
209
182
 
@@ -1,7 +1,7 @@
1
- const { getNames } = require('./utils')
1
+ const { getNames, extractExportName } = require('./utils')
2
2
 
3
3
  // https://regex101.com/r/example/1
4
- const EXPORT_DEFAULT_REGEX = /export\s+default\s+(?:function\s+([\w$]+)|class\s+([\w$]+)|([\w$]+|\([^)]*\)\s*=>|{[\s\S]*?}|\[[\s\S]*?\]|[\w$]+(?:\s*[+\-*/%]\s*[\w$]+)*))/gm
4
+ const EXPORT_DEFAULT_REGEX = /export\s+default\s+(?:(?:async\s+)?function\s+([\w$]+)|class\s+([\w$]+)|([\w$]+|\([^)]*\)\s*=>|{[\s\S]*?}|\[[\s\S]*?\]|'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`|[\w$]+(?:\s*[+\-*/%]\s*[\w$]+)*))/gm
5
5
  // https://regex101.com/r/example/2
6
6
  const EXPORT_NAMED_REGEX = /export\s+(?:const|let|var|function|class|async\s+function)\s+([\w$]+)/gm
7
7
  // https://regex101.com/r/example/3
@@ -53,27 +53,52 @@ function getDefaultExports(targetText = '') {
53
53
  var result
54
54
  while((result = EXPORT_DEFAULT_REGEX.exec(targetText)) !== null) {
55
55
  const [ line, functionName, className, otherValue ] = result
56
- let exportName, exportValue
57
-
56
+ let exportName, exportValue, exportDefinitionName, completeLine
57
+
58
58
  if (functionName) {
59
- exportName = functionName
60
- exportValue = `function ${functionName}`
59
+ exportName = ''
60
+ exportDefinitionName = functionName
61
+ // Extract complete line from original text (including parameters)
62
+ const lineStart = result.index
63
+ const lineEnd = targetText.indexOf('\n', lineStart)
64
+ completeLine = targetText.substring(lineStart, lineEnd === -1 ? targetText.length : lineEnd).trim()
65
+ exportValue = completeLine.replace(/^export\s+default\s+/, '')
61
66
  } else if (className) {
62
- exportName = className
63
- exportValue = `class ${className}`
67
+ exportName = ''
68
+ exportDefinitionName = className
69
+ // Extract complete line from original text
70
+ const lineStart = result.index
71
+ const lineEnd = targetText.indexOf('\n', lineStart)
72
+ completeLine = targetText.substring(lineStart, lineEnd === -1 ? targetText.length : lineEnd).trim()
73
+ exportValue = completeLine.replace(/^export\s+default\s+/, '')
64
74
  } else {
65
- exportName = extractExportName(otherValue)
75
+ exportDefinitionName = extractExportName(otherValue)
76
+ exportName = ''
66
77
  exportValue = otherValue
78
+ completeLine = line
67
79
  }
68
-
69
- exportStatements.push({
70
- line: line.trim(),
80
+
81
+ const exportStatement = {
82
+ line: completeLine.trim(),
71
83
  exportType: 'default',
72
84
  exportName,
85
+ exportDefinitionName,
73
86
  exportValue: exportValue.trim(),
74
87
  index: result.index,
75
88
  isExport: true
76
- })
89
+ }
90
+
91
+ // Parse object literals to extract definitions and keys
92
+ const trimmedValue = exportValue.trim()
93
+ if (trimmedValue.startsWith('{') && trimmedValue.endsWith('}')) {
94
+ const definitions = getNames(trimmedValue, 'definition')
95
+ if (definitions) {
96
+ exportStatement.definitions = definitions
97
+ exportStatement.keys = definitions.destructuredParameters.map(param => param.name.replace(/,+$/, ''))
98
+ }
99
+ }
100
+
101
+ exportStatements.push(exportStatement)
77
102
  }
78
103
  return exportStatements
79
104
  }
@@ -200,33 +225,6 @@ function getAssertionExports(targetText = '') {
200
225
  return exportStatements
201
226
  }
202
227
 
203
- function extractExportName(exportValue) {
204
- // Handle function declarations
205
- if (exportValue.includes('function') && !exportValue.includes('=>')) {
206
- const match = exportValue.match(/function\s+([\w$]+)/)
207
- return match ? match[1] : 'anonymous'
208
- }
209
-
210
- // Handle class declarations
211
- if (exportValue.includes('class')) {
212
- const match = exportValue.match(/class\s+([\w$]+)/)
213
- return match ? match[1] : 'anonymous'
214
- }
215
-
216
- // Handle arrow functions
217
- if (exportValue.includes('=>')) {
218
- return 'anonymous'
219
- }
220
-
221
- // Handle object/array literals
222
- if (exportValue.startsWith('{') || exportValue.startsWith('[')) {
223
- return 'literal'
224
- }
225
-
226
- // Handle simple values
227
- return exportValue.trim()
228
- }
229
-
230
228
  function parseExportsList(exportsList) {
231
229
  const exports = []
232
230
  const items = exportsList.split(',').map(item => item.trim())
package/src/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const { getImports, getStaticImports, getDynamicImports } = require('./get-imports')
2
2
  const { getRequires, getStaticRequires, getDynamicRequires } = require('./get-requires')
3
3
  const { getCjsExports } = require('./get-cjs-exports')
4
+ const { detectSyntax, hasESMSyntax, hasCJSSyntax } = require('./detect-syntax')
4
5
  const {
5
6
  getExports,
6
7
  getDefaultExports,
@@ -80,6 +81,10 @@ const { execSync, spawnSync } = require('child_process')
80
81
 
81
82
  module.exports = {
82
83
  extractDeps,
84
+ /* syntax detection */
85
+ hasESMSyntax,
86
+ hasCJSSyntax,
87
+ detectSyntax,
83
88
  /* Requires */
84
89
  getRequires,
85
90
  getDynamicRequires,
package/src/utils.js CHANGED
@@ -155,8 +155,48 @@ function getNames(names, aliasKey = 'alias') {
155
155
  }
156
156
 
157
157
 
158
+ function extractExportName(exportValue) {
159
+ // Handle function declarations
160
+ if (exportValue.includes('function') && !exportValue.includes('=>')) {
161
+ const match = exportValue.match(/function\s+([\w$]+)/)
162
+ return match ? match[1] : 'anonymous'
163
+ }
164
+
165
+ // Handle class declarations
166
+ if (exportValue.includes('class')) {
167
+ const match = exportValue.match(/class\s+([\w$]+)/)
168
+ return match ? match[1] : 'anonymous'
169
+ }
170
+
171
+ // Handle arrow functions
172
+ if (exportValue.includes('=>')) {
173
+ return 'anonymous'
174
+ }
175
+
176
+ // Handle object/array literals
177
+ if (exportValue.startsWith('{')) {
178
+ return 'literal-object'
179
+ }
180
+
181
+ if (exportValue.startsWith('[')) {
182
+ return 'literal-array'
183
+ }
184
+
185
+ if (exportValue.match(/^\d+$/)) {
186
+ return 'literal-number'
187
+ }
188
+
189
+ if (exportValue.match(/^['"]/)) {
190
+ return 'literal-string'
191
+ }
192
+
193
+ // Handle simple values
194
+ return exportValue.trim()
195
+ }
196
+
158
197
  module.exports = {
159
198
  isRelativeImport,
160
199
  isRelativeImportWithQuotes,
161
- getNames
200
+ getNames,
201
+ extractExportName
162
202
  }
@@ -36,7 +36,7 @@ module.exports = () => {}
36
36
  assert.equal(classExport.exportValue, 'class MyClass {}')
37
37
 
38
38
  // Check object literal
39
- const objectExport = foundExports.find(exp => exp.exportDefinitionName === 'literal')
39
+ const objectExport = foundExports.find(exp => exp.exportDefinitionName === 'literal-object')
40
40
  assert.ok(objectExport, 'Should find object literal export')
41
41
  assert.equal(objectExport.exportValue, "{ foo: 'bar' }")
42
42
 
@@ -536,4 +536,56 @@ module.exports = {
536
536
  assert.ok(foundExports[0].keys.includes('environment'), 'Keys should include environment')
537
537
  })
538
538
 
539
+
540
+ test('lambda function default object', () => {
541
+ const code = `
542
+ const handler = async event => {
543
+ return {
544
+ statusCode: 200,
545
+ body: JSON.stringify(
546
+ {
547
+ message: 'yo function',
548
+ input: event,
549
+ },
550
+ null,
551
+ 2
552
+ ),
553
+ };
554
+ };
555
+
556
+ module.exports = {
557
+ handler,
558
+ }
559
+ `
560
+
561
+ const foundExports = getCjsExports(code)
562
+ console.log('foundExports', foundExports)
563
+
564
+ assert.ok(foundExports.length === 1, 'Should find 1 export')
565
+ assert.ok(foundExports[0].keys, 'Should have keys array')
566
+ assert.equal(foundExports[0].keys.length, 1, 'Should have 1 key')
567
+ assert.equal(foundExports[0].keys[0], 'handler', 'Key should be "handler" without trailing comma')
568
+ })
569
+
570
+ test('lambda function default function inline', () => {
571
+ const code = `
572
+ module.exports = async function handler(event) {
573
+ return {
574
+ statusCode: 200,
575
+ body: JSON.stringify({
576
+ message: 'yo function',
577
+ input: event,
578
+ }, null, 2),
579
+ }
580
+ }
581
+ `
582
+
583
+ const foundExports = getCjsExports(code)
584
+ console.log('foundExports', foundExports)
585
+
586
+ assert.ok(foundExports.length === 1, 'Should find 1 export')
587
+ assert.equal(foundExports[0].exportType, 'default', 'exportType should be default')
588
+ assert.equal(foundExports[0].exportName, '', 'exportName should empty because it default function')
589
+ })
590
+
539
591
  test.run()
@@ -23,40 +23,51 @@ export default () => {}
23
23
  `
24
24
 
25
25
  const foundExports = getDefaultExports(code)
26
+ console.log('foundExports', foundExports)
26
27
 
27
- assert.ok(foundExports.length === 6, 'Should find 6 default exports')
28
+ assert.ok(foundExports.length === 7, 'Should find 7 default exports')
28
29
 
29
30
  // Check function export
30
- const functionExport = foundExports.find(exp => exp.exportName === 'myFunction')
31
+ const functionExport = foundExports.find(exp => exp.exportDefinitionName === 'myFunction')
31
32
  assert.ok(functionExport, 'Should find function export')
32
33
  assert.equal(functionExport.exportType, 'default')
33
- assert.equal(functionExport.exportValue, 'function myFunction')
34
+ assert.equal(functionExport.exportValue, 'function myFunction() {}')
34
35
 
35
36
  // Check class export
36
- const classExport = foundExports.find(exp => exp.exportName === 'MyClass')
37
+ const classExport = foundExports.find(exp => exp.exportDefinitionName === 'MyClass')
37
38
  assert.ok(classExport, 'Should find class export')
38
39
  assert.equal(classExport.exportType, 'default')
39
- assert.equal(classExport.exportValue, 'class MyClass')
40
+ assert.equal(classExport.exportValue, 'class MyClass {}')
40
41
 
41
42
  // Check object literal
42
- const objectExport = foundExports.find(exp => exp.exportName === 'literal')
43
+ const objectExport = foundExports.find(exp => exp.exportDefinitionName === 'literal-object')
43
44
  assert.ok(objectExport, 'Should find object literal export')
44
45
  assert.equal(objectExport.exportValue, "{ foo: 'bar' }")
46
+ assert.equal(objectExport.exportName, '')
47
+
48
+ // Check string literal
49
+ const stringExport = foundExports.find(exp => exp.exportDefinitionName === 'literal-string')
50
+ assert.ok(stringExport, 'Should find string literal export')
51
+ assert.equal(stringExport.exportValue, "'hello'", 'exportValue should be "hello"')
52
+ assert.equal(stringExport.exportName, '', 'exportName should be empty')
45
53
 
46
54
  // Check array literal
47
55
  const arrayExport = foundExports.find(exp => exp.exportValue === '[1, 2, 3]')
48
56
  assert.ok(arrayExport, 'Should find array literal export')
49
- assert.equal(arrayExport.exportName, 'literal')
57
+ assert.equal(arrayExport.exportDefinitionName, 'literal-array')
58
+ assert.equal(arrayExport.exportName, '')
50
59
 
51
60
  // Check number literal
52
61
  const numberExport = foundExports.find(exp => exp.exportValue === '42')
53
62
  assert.ok(numberExport, 'Should find number literal export')
54
- assert.equal(numberExport.exportName, '42')
63
+ assert.equal(numberExport.exportDefinitionName, 'literal-number')
64
+ assert.equal(numberExport.exportName, '')
55
65
 
56
66
  // Check arrow function
57
67
  const arrowExport = foundExports.find(exp => exp.exportValue === '() =>')
58
68
  assert.ok(arrowExport, 'Should find arrow function export')
59
- assert.equal(arrowExport.exportName, 'anonymous')
69
+ assert.equal(arrowExport.exportDefinitionName, 'anonymous')
70
+ assert.equal(arrowExport.exportName, '')
60
71
  })
61
72
 
62
73
  test('should extract named exports', () => {
@@ -318,7 +329,7 @@ export {
318
329
  assert.ok(foundExports.length > 0, 'Should find exports in complex code')
319
330
 
320
331
  // Should handle anonymous functions and classes
321
- const anonymousExports = foundExports.filter(exp => exp.exportName === 'anonymous')
332
+ const anonymousExports = foundExports.filter(exp => exp.exportDefinitionName === 'anonymous')
322
333
  assert.ok(anonymousExports.length >= 2, 'Should find anonymous function and class exports')
323
334
 
324
335
  // Should handle complex named declaration exports
@@ -380,4 +391,145 @@ export { MyComponent, Button, Input }
380
391
  assert.ok(thirdExport.exportType === 'named', 'Should find named export')
381
392
  })
382
393
 
394
+
395
+ test('lambda function default function', () => {
396
+ const code = `
397
+ const handler = async event => {
398
+ return {
399
+ statusCode: 200,
400
+ body: JSON.stringify(
401
+ {
402
+ message: 'yo function',
403
+ input: event,
404
+ },
405
+ null,
406
+ 2
407
+ ),
408
+ };
409
+ };
410
+ export default handler
411
+ `
412
+
413
+ const foundExports = getExports(code)
414
+ console.log('foundExports', foundExports)
415
+
416
+ // TODO add assertions
417
+ assert.ok(foundExports.length === 1, 'Should find 1 export')
418
+ assert.ok(foundExports[0].exportType, 'Should have exportType array')
419
+ assert.equal(foundExports[0].exportType, 'default', 'exportType should be default')
420
+ assert.equal(foundExports[0].exportName, '', 'exportName should empty because it default function')
421
+ })
422
+
423
+
424
+ test('lambda function default function inline', () => {
425
+ const code = `
426
+ export default async function handler(event) {
427
+ return {
428
+ statusCode: 200,
429
+ body: JSON.stringify({
430
+ message: 'yo function',
431
+ input: event,
432
+ }, null, 2),
433
+ }
434
+ }
435
+ `
436
+
437
+ const foundExports = getExports(code)
438
+ console.log('foundExports', foundExports)
439
+
440
+ assert.ok(foundExports.length === 1, 'Should find 1 export')
441
+ assert.ok(foundExports[0].exportType, 'Should have keys array')
442
+ assert.equal(foundExports[0].exportType, 'default', 'exportType should be default')
443
+ assert.equal(foundExports[0].exportName, '', 'exportName should empty because it default function')
444
+ })
445
+
446
+ test('lambda function default object', () => {
447
+ const code = `
448
+ const handler = async event => {
449
+ return {
450
+ statusCode: 200,
451
+ body: JSON.stringify(
452
+ {
453
+ message: 'yo function',
454
+ input: event,
455
+ },
456
+ null,
457
+ 2
458
+ ),
459
+ };
460
+ };
461
+ export default {
462
+ handler,
463
+ }
464
+ `
465
+
466
+ const foundExports = getExports(code)
467
+ console.log('foundExports', foundExports)
468
+
469
+ assert.ok(foundExports.length === 1, 'Should find 1 export')
470
+ assert.ok(foundExports[0].keys, 'Should have keys array')
471
+ assert.equal(foundExports[0].keys.length, 1, 'Should have 1 key')
472
+ assert.equal(foundExports[0].keys[0], 'handler', 'Key should be "handler" without trailing comma')
473
+ })
474
+
475
+
476
+ test('lambda function named object', () => {
477
+ const code = `
478
+ const handler = async event => {
479
+ return {
480
+ statusCode: 200,
481
+ body: JSON.stringify(
482
+ {
483
+ message: 'yo function',
484
+ input: event,
485
+ },
486
+ null,
487
+ 2
488
+ ),
489
+ };
490
+ };
491
+ export {
492
+ handler,
493
+ }
494
+ `
495
+
496
+ const foundExports = getExports(code)
497
+ console.log('foundExports', foundExports)
498
+
499
+ assert.ok(foundExports.length === 1, 'Should find 1 export')
500
+ assert.ok(foundExports[0].exportType, 'Should have keys array')
501
+ assert.equal(foundExports[0].exportType, 'named', 'exportType should be named')
502
+ assert.equal(foundExports[0].exportName, 'handler', 'exportName should be "handler" without trailing comma')
503
+ })
504
+
505
+ test('lambda function named object with alias', () => {
506
+ const code = `
507
+ const handler = async event => {
508
+ return {
509
+ statusCode: 200,
510
+ body: JSON.stringify(
511
+ {
512
+ message: 'yo function',
513
+ input: event,
514
+ },
515
+ null,
516
+ 2
517
+ ),
518
+ };
519
+ };
520
+ export {
521
+ handler as myHandler
522
+ }
523
+ `
524
+
525
+ const foundExports = getExports(code)
526
+ console.log('foundExports', foundExports)
527
+
528
+ assert.ok(foundExports.length === 1, 'Should find 1 export')
529
+ assert.ok(foundExports[0].exportType, 'Should have keys array')
530
+ assert.equal(foundExports[0].exportType, 'named', 'exportType should be named')
531
+ assert.equal(foundExports[0].exportName, 'handler', 'exportName should be "handler" without trailing comma')
532
+ assert.equal(foundExports[0].exportAlias, 'myHandler', 'exportAlias should be "myHandler" without trailing comma')
533
+ })
534
+
383
535
  test.run()