@davidwells/extract-deps 0.0.1

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.
Files changed (59) hide show
  1. package/README.md +211 -0
  2. package/_usage.js +175 -0
  3. package/examples/get-exports.js +31 -0
  4. package/package.json +27 -0
  5. package/src/detect-syntax.js +70 -0
  6. package/src/get-exports.js +299 -0
  7. package/src/get-imports.js +165 -0
  8. package/src/get-requires.js +104 -0
  9. package/src/index.js +90 -0
  10. package/src/utils.js +64 -0
  11. package/tests/detect-syntax.test.js +280 -0
  12. package/tests/get-exports.test.js +346 -0
  13. package/tests/get-imports.test.js +395 -0
  14. package/tests/get-requires.test.js +201 -0
  15. package/tests/index.test.js +202 -0
  16. package/tests/test-utils.js +30 -0
  17. package/tsconfig.json +19 -0
  18. package/types/detect-syntax.d.ts +39 -0
  19. package/types/detect-syntax.d.ts.map +1 -0
  20. package/types/extract-deps/_usage.d.ts +20 -0
  21. package/types/extract-deps/_usage.d.ts.map +1 -0
  22. package/types/extract-deps/examples/get-exports.d.ts +2 -0
  23. package/types/extract-deps/examples/get-exports.d.ts.map +1 -0
  24. package/types/extract-deps/src/detect-syntax.d.ts +39 -0
  25. package/types/extract-deps/src/detect-syntax.d.ts.map +1 -0
  26. package/types/extract-deps/src/get-exports.d.ts +52 -0
  27. package/types/extract-deps/src/get-exports.d.ts.map +1 -0
  28. package/types/extract-deps/src/get-imports.d.ts +53 -0
  29. package/types/extract-deps/src/get-imports.d.ts.map +1 -0
  30. package/types/extract-deps/src/get-requires.d.ts +34 -0
  31. package/types/extract-deps/src/get-requires.d.ts.map +1 -0
  32. package/types/extract-deps/src/index.d.ts +39 -0
  33. package/types/extract-deps/src/index.d.ts.map +1 -0
  34. package/types/extract-deps/src/utils.d.ts +4 -0
  35. package/types/extract-deps/src/utils.d.ts.map +1 -0
  36. package/types/extract-deps/tests/detect-syntax.test.d.ts +2 -0
  37. package/types/extract-deps/tests/detect-syntax.test.d.ts.map +1 -0
  38. package/types/extract-deps/tests/get-exports.test.d.ts +2 -0
  39. package/types/extract-deps/tests/get-exports.test.d.ts.map +1 -0
  40. package/types/extract-deps/tests/get-imports.test.d.ts +2 -0
  41. package/types/extract-deps/tests/get-imports.test.d.ts.map +1 -0
  42. package/types/extract-deps/tests/get-requires.test.d.ts +2 -0
  43. package/types/extract-deps/tests/get-requires.test.d.ts.map +1 -0
  44. package/types/extract-deps/tests/index.test.d.ts +18 -0
  45. package/types/extract-deps/tests/index.test.d.ts.map +1 -0
  46. package/types/extract-deps/tests/test-utils.d.ts +9 -0
  47. package/types/extract-deps/tests/test-utils.d.ts.map +1 -0
  48. package/types/get-exports.d.ts +52 -0
  49. package/types/get-exports.d.ts.map +1 -0
  50. package/types/get-imports.d.ts +53 -0
  51. package/types/get-imports.d.ts.map +1 -0
  52. package/types/get-requires.d.ts +34 -0
  53. package/types/get-requires.d.ts.map +1 -0
  54. package/types/index.d.ts +39 -0
  55. package/types/index.d.ts.map +1 -0
  56. package/types/packages-list/index.d.ts +138 -0
  57. package/types/packages-list/index.d.ts.map +1 -0
  58. package/types/utils.d.ts +4 -0
  59. package/types/utils.d.ts.map +1 -0
package/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # extract-deps
2
+
3
+ A lightweight utility for extracting imports, requires, and exports from JavaScript/TypeScript code without using AST parsing.
4
+
5
+ ## Features
6
+
7
+ - Extract ES6 imports (static and dynamic)
8
+ - Extract CommonJS requires (static and dynamic)
9
+ - Extract ES6 exports (default, named, namespace, type exports)
10
+ - Parse code without AST for better performance
11
+ - Support for destructuring in imports/requires
12
+ - TypeScript support for type exports and imports
13
+ - Handles complex patterns like re-exports and aliased exports
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @davidwells/extract-deps
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Basic Usage
24
+
25
+ ```javascript
26
+ const { extractDeps } = require('@davidwells/extract-deps')
27
+
28
+ const code = `
29
+ import { test } from 'uvu'
30
+ import assert from 'uvu/assert'
31
+ const { execSync } = require('child_process')
32
+ const fs = require('fs')
33
+
34
+ export const myFunction = () => {}
35
+ export default class MyClass {}
36
+ `
37
+
38
+ const { imports, requires, exports } = extractDeps(code)
39
+
40
+ console.log('Imports:', imports)
41
+ console.log('Requires:', requires)
42
+ console.log('Exports:', exports)
43
+ ```
44
+
45
+ ### Extract Imports Only
46
+
47
+ ```javascript
48
+ const { getImports, getStaticImports, getDynamicImports } = require('@davidwells/extract-deps')
49
+
50
+ const code = `
51
+ import { test } from 'uvu'
52
+ import assert from 'uvu/assert'
53
+
54
+ // Dynamic imports
55
+ const dynamicModule = await import('./utils')
56
+ const config = await import('./config.js')
57
+ `
58
+
59
+ // Get all imports
60
+ const allImports = getImports(code)
61
+
62
+ // Get only static imports
63
+ const staticImports = getStaticImports(code)
64
+
65
+ // Get only dynamic imports
66
+ const dynamicImports = getDynamicImports(code)
67
+
68
+ console.log('All imports:', allImports)
69
+ console.log('Static imports:', staticImports)
70
+ console.log('Dynamic imports:', dynamicImports)
71
+ ```
72
+
73
+ ### Extract Requires Only
74
+
75
+ ```javascript
76
+ const { getRequires, getStaticRequires, getDynamicRequires } = require('@davidwells/extract-deps')
77
+
78
+ const code = `
79
+ const { test } = require('uvu')
80
+ const assert = require('uvu/assert')
81
+ const fs = require('fs')
82
+ `
83
+
84
+ // Get all requires
85
+ const allRequires = getRequires(code)
86
+
87
+ // Get only static requires
88
+ const staticRequires = getStaticRequires(code)
89
+
90
+ // Get only dynamic requires
91
+ const dynamicRequires = getDynamicRequires(code)
92
+
93
+ console.log('All requires:', allRequires)
94
+ console.log('Static requires:', staticRequires)
95
+ console.log('Dynamic requires:', dynamicRequires)
96
+ ```
97
+
98
+ ### Extract Exports Only
99
+
100
+ ```javascript
101
+ const {
102
+ getExports,
103
+ getDefaultExports,
104
+ getNamedExports,
105
+ getNamespaceExports,
106
+ getTypeExports
107
+ } = require('@davidwells/extract-deps')
108
+
109
+ const code = `
110
+ // Default exports
111
+ export default function myFunction() {}
112
+ export default class MyClass {}
113
+
114
+ // Named exports
115
+ export const myVariable = 'value'
116
+ export function myFunction() {}
117
+
118
+ // Named declaration exports
119
+ export { myFunction, anotherFunction }
120
+ export { myFunction as aliasedFunction }
121
+
122
+ // Re-exports
123
+ export * from './module'
124
+ export * as namespace from './module'
125
+
126
+ // Type exports
127
+ export type MyType = string
128
+ export interface MyInterface {}
129
+ `
130
+
131
+ // Get all exports
132
+ const allExports = getExports(code)
133
+
134
+ // Get specific export types
135
+ const defaultExports = getDefaultExports(code)
136
+ const namedExports = getNamedExports(code)
137
+ const namespaceExports = getNamespaceExports(code)
138
+ const typeExports = getTypeExports(code)
139
+
140
+ console.log('All exports:', allExports)
141
+ console.log('Default exports:', defaultExports)
142
+ console.log('Named exports:', namedExports)
143
+ console.log('Namespace exports:', namespaceExports)
144
+ console.log('Type exports:', typeExports)
145
+ ```
146
+
147
+ ## API Reference
148
+
149
+ ### `extractDeps(code)`
150
+
151
+ Main function that extracts all imports, requires, and exports from the given code.
152
+
153
+ **Parameters:**
154
+ - `code` (string): The JavaScript/TypeScript code to analyze
155
+
156
+ **Returns:**
157
+ - Object with `imports`, `requires`, and `exports` arrays
158
+
159
+ ### Import Functions
160
+
161
+ - `getImports(code)` - Get all imports (static and dynamic)
162
+ - `getStaticImports(code)` - Get only static imports
163
+ - `getDynamicImports(code)` - Get only dynamic imports
164
+
165
+ ### Require Functions
166
+
167
+ - `getRequires(code)` - Get all requires (static and dynamic)
168
+ - `getStaticRequires(code)` - Get only static requires
169
+ - `getDynamicRequires(code)` - Get only dynamic requires
170
+
171
+ ### Export Functions
172
+
173
+ - `getExports(code)` - Get all exports
174
+ - `getDefaultExports(code)` - Get only default exports
175
+ - `getNamedExports(code)` - Get only named exports
176
+ - `getNamedDeclarationExports(code)` - Get named declaration exports
177
+ - `getAllExports(code)` - Get all exports (alias for getExports)
178
+ - `getNamespaceExports(code)` - Get namespace exports (export * as)
179
+ - `getTypeExports(code)` - Get TypeScript type exports
180
+
181
+ ## Data Structure
182
+
183
+ Each import/require/export object contains:
184
+
185
+ ```javascript
186
+ {
187
+ moduleSpecifier: string, // The module being imported/required
188
+ exportType: string, // Type of export (for exports)
189
+ exportName: string, // Name of export (for exports)
190
+ definitions: Array, // Destructuring and variable definitions
191
+ // ... other properties specific to the type
192
+ }
193
+ ```
194
+
195
+ ## Examples
196
+
197
+ You can find more examples in the `examples/` directory and usage examples in `_usage.js`.
198
+
199
+ ## Performance
200
+
201
+ This library avoids AST parsing for better performance when you only need to extract dependency information. It uses regex-based parsing optimized for common JavaScript/TypeScript patterns.
202
+
203
+ ## Alternative Approaches
204
+
205
+ - Runtime analysis: [pkg-exports](https://github.com/antfu/pkg-exports/)
206
+ - AST-based parsing: [parse-imports](https://github.com/import-js/parse-imports)
207
+ - ESM/CJS utils https://www.npmjs.com/package/mlly
208
+
209
+ ## License
210
+
211
+ MIT
package/_usage.js ADDED
@@ -0,0 +1,175 @@
1
+ const path = require('path')
2
+ const fs = require('fs')
3
+ const { extractDeps } = require('./src/index')
4
+
5
+ /**
6
+ * Example usage of extract-deps with lerna fixture
7
+ * This demonstrates how to analyze dependencies in a monorepo structure
8
+ */
9
+
10
+ // Import the lerna fixture directory
11
+ const lernaFixturePath = path.join(__dirname, '../packages-build-order/tests/lerna-fixture')
12
+
13
+ /**
14
+ * Read and parse a package.json file
15
+ * @param {string} filePath - Path to the package.json file
16
+ * @returns {Object} Parsed package.json content
17
+ */
18
+ function readPackageJson(filePath) {
19
+ try {
20
+ const content = fs.readFileSync(filePath, 'utf8')
21
+ return JSON.parse(content)
22
+ } catch (error) {
23
+ console.error(`Error reading package.json at ${filePath}:`, error.message)
24
+ return null
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Get all package.json files in the lerna fixture
30
+ * @returns {Array} Array of package.json file paths
31
+ */
32
+ function getPackageJsonFiles() {
33
+ const packages = []
34
+
35
+ // Get root package.json
36
+ const rootPackageJson = path.join(lernaFixturePath, 'package.json')
37
+ if (fs.existsSync(rootPackageJson)) {
38
+ packages.push({ path: rootPackageJson, type: 'root' })
39
+ }
40
+
41
+ // Get all subdirectory package.json files
42
+ const dirs = fs.readdirSync(lernaFixturePath, { withFileTypes: true })
43
+
44
+ dirs.forEach(dirent => {
45
+ if (dirent.isDirectory()) {
46
+ const packageJsonPath = path.join(lernaFixturePath, dirent.name, 'package.json')
47
+ if (fs.existsSync(packageJsonPath)) {
48
+ packages.push({ path: packageJsonPath, type: 'package', name: dirent.name })
49
+ }
50
+ }
51
+ })
52
+
53
+ return packages
54
+ }
55
+
56
+ /**
57
+ * Analyze dependencies in the lerna fixture
58
+ */
59
+ function analyzeLernaFixture() {
60
+ console.log('🔍 Analyzing Lerna Fixture Dependencies\n')
61
+
62
+ const packageFiles = getPackageJsonFiles()
63
+
64
+ packageFiles.forEach(({ path: filePath, type, name }) => {
65
+ const packageJson = readPackageJson(filePath)
66
+ if (!packageJson) return
67
+
68
+ console.log(`📦 ${type === 'root' ? 'Root Package' : `Package: ${name}`}`)
69
+ console.log(` Version: ${packageJson.version}`)
70
+ console.log(` Path: ${filePath}`)
71
+
72
+ // Analyze dependencies
73
+ if (packageJson.dependencies) {
74
+ console.log(` Dependencies (${Object.keys(packageJson.dependencies).length}):`)
75
+ Object.entries(packageJson.dependencies).forEach(([dep, version]) => {
76
+ console.log(` - ${dep}: ${version}`)
77
+ })
78
+ }
79
+
80
+ if (packageJson.devDependencies) {
81
+ console.log(` Dev Dependencies (${Object.keys(packageJson.devDependencies).length}):`)
82
+ Object.entries(packageJson.devDependencies).forEach(([dep, version]) => {
83
+ console.log(` - ${dep}: ${version}`)
84
+ })
85
+ }
86
+
87
+ console.log('')
88
+ })
89
+ }
90
+
91
+ /**
92
+ * Example of using extractDeps with actual code files
93
+ */
94
+ function analyzeCodeDependencies() {
95
+ console.log('🔍 Analyzing Code Dependencies\n')
96
+
97
+ // Example code that might be found in the lerna fixture
98
+ const exampleCode = `
99
+ const { test } = require('uvu')
100
+ const assert = require('uvu/assert')
101
+ import { extractDeps } from './src/index'
102
+ import path from 'path'
103
+ import fs from 'fs'
104
+
105
+ // Dynamic imports
106
+ const dynamicModule = await import('./utils')
107
+ const config = await import('./config.js')
108
+
109
+ // More requires
110
+ const lerna = require('lerna')
111
+ const { execSync } = require('child_process')
112
+ `
113
+
114
+ console.log('📝 Example Code:')
115
+ console.log(exampleCode)
116
+ console.log('')
117
+
118
+ const { imports, requires } = extractDeps(exampleCode)
119
+
120
+ console.log('📊 Extracted Dependencies:')
121
+ console.log(` Imports (${imports.length}):`)
122
+ imports.forEach((imp, index) => {
123
+ console.log(` ${index + 1}. ${imp.moduleSpecifier}`)
124
+ if (imp.definitions && imp.definitions[0] && imp.definitions[0].destructuredParameters) {
125
+ const params = imp.definitions[0].destructuredParameters
126
+ console.log(` Destructured: ${params.map(p => p.name).join(', ')}`)
127
+ }
128
+ })
129
+
130
+ console.log(` Requires (${requires.length}):`)
131
+ requires.forEach((req, index) => {
132
+ console.log(` ${index + 1}. ${req.moduleSpecifier}`)
133
+ if (req.definitions && req.definitions[0] && req.definitions[0].destructuredParameters) {
134
+ const params = req.definitions[0].destructuredParameters
135
+ console.log(` Destructured: ${params.map(p => p.name).join(', ')}`)
136
+ }
137
+ })
138
+ }
139
+
140
+ /**
141
+ * Main function to run the examples
142
+ */
143
+ function main() {
144
+ console.log('🚀 Extract Dependencies - Lerna Fixture Example\n')
145
+ console.log('=' .repeat(50))
146
+
147
+ // Check if lerna fixture exists
148
+ if (!fs.existsSync(lernaFixturePath)) {
149
+ console.error('❌ Lerna fixture not found at:', lernaFixturePath)
150
+ console.log('Please ensure the fixture exists before running this example.')
151
+ return
152
+ }
153
+
154
+ // Analyze the lerna fixture structure
155
+ analyzeLernaFixture()
156
+
157
+ console.log('=' .repeat(50))
158
+
159
+ // Analyze code dependencies
160
+ analyzeCodeDependencies()
161
+
162
+ console.log('✅ Example completed!')
163
+ }
164
+
165
+ // Run the example if this file is executed directly
166
+ if (require.main === module) {
167
+ main()
168
+ }
169
+
170
+ module.exports = {
171
+ analyzeLernaFixture,
172
+ analyzeCodeDependencies,
173
+ readPackageJson,
174
+ getPackageJsonFiles
175
+ }
@@ -0,0 +1,31 @@
1
+ const { getExports } = require('../src/get-exports')
2
+
3
+ const testCode = `
4
+ // Default exports
5
+ export default function myFunction() {}
6
+ export default class MyClass {}
7
+ export default { foo: 'bar' }
8
+
9
+ // Named exports
10
+ export const myVariable = 'value'
11
+ export function myFunction() {}
12
+ export class MyClass {}
13
+
14
+ // Named declaration exports
15
+ export { myFunction, anotherFunction }
16
+ export { myFunction as aliasedFunction }
17
+
18
+ // Re-exports
19
+ export * from './module'
20
+ export * as namespace from './module'
21
+
22
+ // Type exports
23
+ export type MyType = string
24
+ export interface MyInterface {}
25
+ `
26
+
27
+ const exportsFound = getExports(testCode)
28
+ console.log('Found exports:')
29
+ exportsFound.forEach((exp, index) => {
30
+ console.log(`${index + 1}. ${exp.exportType}: ${exp.exportName || exp.namespace || 'default'}`)
31
+ })
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@davidwells/extract-deps",
3
+ "version": "0.0.1",
4
+ "description": "Get imports and requires from a codebase without AST",
5
+ "main": "src/index.js",
6
+ "types": "types/index.d.ts",
7
+ "author": "David Wells",
8
+ "license": "MIT",
9
+ "scripts": {
10
+ "watch": "watchlist index.js -- npm test",
11
+ "test": "npm run test:tests",
12
+ "test:examples": "uvu examples \".*\\.test.js$\"",
13
+ "test:tests": "uvu tests \".*\\.test.js$\"",
14
+ "build": "npm run types",
15
+ "types": "tsc --emitDeclarationOnly --declaration --outDir types",
16
+ "release:patch": "npm version patch && npm publish",
17
+ "release:minor": "npm version minor && npm publish",
18
+ "release:major": "npm version major && npm publish"
19
+ },
20
+ "dependencies": {
21
+ "inspect-parameters-declaration": "^0.1.0"
22
+ },
23
+ "devDependencies": {
24
+ "typescript": "^5.7.3",
25
+ "uvu": "^0.5.6"
26
+ }
27
+ }
@@ -0,0 +1,70 @@
1
+ // via https://github.com/unjs/mlly/blob/6bf5ad4d784e314319f872a8df41d64edd4e5505/src/syntax.ts#L7
2
+
3
+ const ESM_RE =
4
+ /([\s;]|^)(import\b[\s\w*,{}]*from|import\b\s*["'*{]|export\b\s*(?:[*{]|default|class|type|interface|enum|function|const|var|let|async function)|import\.meta\b)/m;
5
+
6
+ const CJS_RE =
7
+ /([\s;]|^)(module\.exports\b|exports\.\w|require\s*\(|global\.\w)/m;
8
+
9
+ const COMMENT_RE = /\/\*.+?\*\/|\/\/.*(?:\r?\n|$)/g;
10
+
11
+ const BUILTIN_EXTENSIONS = new Set([".mjs", ".cjs", ".node", ".wasm"]);
12
+
13
+ /**
14
+ * Options for detecting syntax within a code string.
15
+ * @typedef {Object} DetectSyntaxOptions
16
+ * @property {boolean} [stripComments=false] - Indicates whether comments should be stripped from the code before syntax checking.
17
+ */
18
+
19
+ /**
20
+ * Determines if a given code string contains ECMAScript module syntax.
21
+ *
22
+ * @param {string} code - The source code to analyze.
23
+ * @param {DetectSyntaxOptions} opts - See {@link DetectSyntaxOptions}.
24
+ * @returns {boolean} `true` if the code contains ESM syntax, otherwise `false`.
25
+ */
26
+ function hasESMSyntax(code, opts = {}) {
27
+ // Always strip comments to avoid false positives from commented syntax
28
+ code = code.replace(COMMENT_RE, "");
29
+ return ESM_RE.test(code);
30
+ }
31
+
32
+ /**
33
+ * Determines if a given string of code contains CommonJS syntax.
34
+ *
35
+ * @param {string} code - The source code to analyze.
36
+ * @param {DetectSyntaxOptions} opts - See {@link DetectSyntaxOptions}.
37
+ * @returns {boolean} `true` if the code contains CommonJS syntax, `false` otherwise.
38
+ */
39
+ function hasCJSSyntax(code, opts = {}) {
40
+ // Always strip comments to avoid false positives from commented syntax
41
+ code = code.replace(COMMENT_RE, "");
42
+ return CJS_RE.test(code);
43
+ }
44
+
45
+ /**
46
+ * Analyzes the supplied code to determine if it contains ECMAScript module syntax, CommonJS syntax, or both.
47
+ *
48
+ * @param {string} code - The source code to analyze.
49
+ * @param {DetectSyntaxOptions} opts - See {@link DetectSyntaxOptions}.
50
+ * @returns {Object} An object indicating the presence of ESM syntax (`hasESM`), CJS syntax (`hasCJS`) and whether both syntaxes are present (`isMixed`).
51
+ */
52
+ function detectSyntax(code, opts = {}) {
53
+ // Always strip comments to avoid false positives from commented syntax
54
+ code = code.replace(COMMENT_RE, "");
55
+ // We strip comments once hence not passing opts down to hasESMSyntax and hasCJSSyntax
56
+ const hasESM = hasESMSyntax(code, {});
57
+ const hasCJS = hasCJSSyntax(code, {});
58
+
59
+ return {
60
+ hasESM,
61
+ hasCJS,
62
+ isMixed: hasESM && hasCJS,
63
+ };
64
+ }
65
+
66
+ module.exports = {
67
+ hasESMSyntax,
68
+ hasCJSSyntax,
69
+ detectSyntax,
70
+ };