@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.
- package/README.md +211 -0
- package/_usage.js +175 -0
- package/examples/get-exports.js +31 -0
- package/package.json +27 -0
- package/src/detect-syntax.js +70 -0
- package/src/get-exports.js +299 -0
- package/src/get-imports.js +165 -0
- package/src/get-requires.js +104 -0
- package/src/index.js +90 -0
- package/src/utils.js +64 -0
- package/tests/detect-syntax.test.js +280 -0
- package/tests/get-exports.test.js +346 -0
- package/tests/get-imports.test.js +395 -0
- package/tests/get-requires.test.js +201 -0
- package/tests/index.test.js +202 -0
- package/tests/test-utils.js +30 -0
- package/tsconfig.json +19 -0
- package/types/detect-syntax.d.ts +39 -0
- package/types/detect-syntax.d.ts.map +1 -0
- package/types/extract-deps/_usage.d.ts +20 -0
- package/types/extract-deps/_usage.d.ts.map +1 -0
- package/types/extract-deps/examples/get-exports.d.ts +2 -0
- package/types/extract-deps/examples/get-exports.d.ts.map +1 -0
- package/types/extract-deps/src/detect-syntax.d.ts +39 -0
- package/types/extract-deps/src/detect-syntax.d.ts.map +1 -0
- package/types/extract-deps/src/get-exports.d.ts +52 -0
- package/types/extract-deps/src/get-exports.d.ts.map +1 -0
- package/types/extract-deps/src/get-imports.d.ts +53 -0
- package/types/extract-deps/src/get-imports.d.ts.map +1 -0
- package/types/extract-deps/src/get-requires.d.ts +34 -0
- package/types/extract-deps/src/get-requires.d.ts.map +1 -0
- package/types/extract-deps/src/index.d.ts +39 -0
- package/types/extract-deps/src/index.d.ts.map +1 -0
- package/types/extract-deps/src/utils.d.ts +4 -0
- package/types/extract-deps/src/utils.d.ts.map +1 -0
- package/types/extract-deps/tests/detect-syntax.test.d.ts +2 -0
- package/types/extract-deps/tests/detect-syntax.test.d.ts.map +1 -0
- package/types/extract-deps/tests/get-exports.test.d.ts +2 -0
- package/types/extract-deps/tests/get-exports.test.d.ts.map +1 -0
- package/types/extract-deps/tests/get-imports.test.d.ts +2 -0
- package/types/extract-deps/tests/get-imports.test.d.ts.map +1 -0
- package/types/extract-deps/tests/get-requires.test.d.ts +2 -0
- package/types/extract-deps/tests/get-requires.test.d.ts.map +1 -0
- package/types/extract-deps/tests/index.test.d.ts +18 -0
- package/types/extract-deps/tests/index.test.d.ts.map +1 -0
- package/types/extract-deps/tests/test-utils.d.ts +9 -0
- package/types/extract-deps/tests/test-utils.d.ts.map +1 -0
- package/types/get-exports.d.ts +52 -0
- package/types/get-exports.d.ts.map +1 -0
- package/types/get-imports.d.ts +53 -0
- package/types/get-imports.d.ts.map +1 -0
- package/types/get-requires.d.ts +34 -0
- package/types/get-requires.d.ts.map +1 -0
- package/types/index.d.ts +39 -0
- package/types/index.d.ts.map +1 -0
- package/types/packages-list/index.d.ts +138 -0
- package/types/packages-list/index.d.ts.map +1 -0
- package/types/utils.d.ts +4 -0
- package/types/utils.d.ts.map +1 -0
package/src/utils.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const { inspectParameters, getParametersNames } = require('inspect-parameters-declaration')
|
|
2
|
+
|
|
3
|
+
function isRelativeImport(importLocation) {
|
|
4
|
+
return !!importLocation.match(/^\./)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function isRelativeImportWithQuotes(importLocation) {
|
|
8
|
+
// TODO harden for "${" or " + 'var' + "
|
|
9
|
+
return !!importLocation.match(/[`"']\./)
|
|
10
|
+
&& importLocation.indexOf('${') === -1 // No template literals
|
|
11
|
+
&& importLocation.indexOf(' + ') === -1 // No string concatenation
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getNames(names) {
|
|
15
|
+
// console.log('names', names)
|
|
16
|
+
// Safety check for undefined or null names
|
|
17
|
+
if (!names || names.trim() === '') {
|
|
18
|
+
return null
|
|
19
|
+
}
|
|
20
|
+
const results = inspectParameters(names).map((item) => {
|
|
21
|
+
item.name = item.parameter
|
|
22
|
+
if (item.destructuredParameters && item.destructuredParameters.length > 0) {
|
|
23
|
+
item.destructuredParameters = item.destructuredParameters.map((details) => {
|
|
24
|
+
if (details.parameter.indexOf(':') > -1) {
|
|
25
|
+
const [ name, alias ] = details.parameter.replace(/^{/, '').replace(/}$/, '').split(':')
|
|
26
|
+
details.name = name.trim()
|
|
27
|
+
details.alias = alias.trim()
|
|
28
|
+
delete details.parameter
|
|
29
|
+
return details
|
|
30
|
+
}
|
|
31
|
+
if (details.declaration.indexOf(' as ') > -1) {
|
|
32
|
+
const [ parameter, alias ] = details.declaration.split(' as ')
|
|
33
|
+
details.name = parameter.trim()
|
|
34
|
+
details.alias = alias.trim()
|
|
35
|
+
delete details.parameter
|
|
36
|
+
return details
|
|
37
|
+
}
|
|
38
|
+
details.name = details.parameter
|
|
39
|
+
delete details.parameter
|
|
40
|
+
return details
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (item.name.indexOf('{') === -1 && item.parameter.indexOf(' as ') > -1) {
|
|
45
|
+
const [ parameter, alias ] = item.parameter.split(' as ')
|
|
46
|
+
item.name = parameter.trim()
|
|
47
|
+
item.alias = alias.trim()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
delete item.parameter
|
|
51
|
+
|
|
52
|
+
return item
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// Return the first (and only) result instead of the array
|
|
56
|
+
return results.length > 0 ? results[0] : null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
module.exports = {
|
|
61
|
+
isRelativeImport,
|
|
62
|
+
isRelativeImportWithQuotes,
|
|
63
|
+
getNames
|
|
64
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
const { test } = require('uvu')
|
|
2
|
+
const assert = require('uvu/assert')
|
|
3
|
+
const { hasESMSyntax, hasCJSSyntax, detectSyntax } = require('../src/detect-syntax.js')
|
|
4
|
+
|
|
5
|
+
// Test data for ESM syntax
|
|
6
|
+
const ESM_EXAMPLES = [
|
|
7
|
+
'import foo from "bar"',
|
|
8
|
+
'import { foo } from "bar"',
|
|
9
|
+
'import * as foo from "bar"',
|
|
10
|
+
'import foo, { bar } from "baz"',
|
|
11
|
+
'export default foo',
|
|
12
|
+
'export { foo }',
|
|
13
|
+
'export * from "bar"',
|
|
14
|
+
'export const foo = "bar"',
|
|
15
|
+
'export function foo() {}',
|
|
16
|
+
'export class Foo {}',
|
|
17
|
+
'export type Foo = string',
|
|
18
|
+
'export async function foo() {}',
|
|
19
|
+
'import.meta.url',
|
|
20
|
+
'import.meta.resolve("foo")'
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
// Test data for CJS syntax
|
|
24
|
+
const CJS_EXAMPLES = [
|
|
25
|
+
'module.exports = foo',
|
|
26
|
+
'module.exports.foo = bar',
|
|
27
|
+
'exports.foo = bar',
|
|
28
|
+
'require("foo")',
|
|
29
|
+
'const foo = require("bar")',
|
|
30
|
+
'global.foo = bar',
|
|
31
|
+
'global.console.log("test")'
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
// Test data for mixed syntax
|
|
35
|
+
const MIXED_EXAMPLES = [
|
|
36
|
+
`import foo from "bar"
|
|
37
|
+
module.exports = foo`,
|
|
38
|
+
`const foo = require("bar")
|
|
39
|
+
export default foo`,
|
|
40
|
+
`export { foo }
|
|
41
|
+
const bar = require("baz")`
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
// Test data for no syntax
|
|
45
|
+
const NO_SYNTAX_EXAMPLES = [
|
|
46
|
+
'const foo = "bar"',
|
|
47
|
+
'function test() { return true }',
|
|
48
|
+
'class Test {}',
|
|
49
|
+
'console.log("hello")',
|
|
50
|
+
'const obj = { foo: "bar" }',
|
|
51
|
+
'if (true) { console.log("test") }'
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
// Test data with comments
|
|
55
|
+
const COMMENTED_EXAMPLES = [
|
|
56
|
+
'// import foo from "bar"',
|
|
57
|
+
'/* export default foo */',
|
|
58
|
+
'const foo = "bar" // module.exports = foo',
|
|
59
|
+
'/* require("foo") */ const bar = "baz"'
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
// Test data with comments followed by actual syntax
|
|
63
|
+
const COMMENTED_WITH_SYNTAX_EXAMPLES = [
|
|
64
|
+
`// This is a comment
|
|
65
|
+
import foo from "bar"`,
|
|
66
|
+
`/* Multi-line comment
|
|
67
|
+
about exports */
|
|
68
|
+
module.exports = foo`
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
test('hasESMSyntax should detect ESM syntax correctly', () => {
|
|
72
|
+
ESM_EXAMPLES.forEach((code, index) => {
|
|
73
|
+
assert.ok(hasESMSyntax(code), `ESM example ${index + 1} should be detected: "${code}"`)
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('hasESMSyntax should not detect non-ESM syntax', () => {
|
|
78
|
+
CJS_EXAMPLES.forEach((code, index) => {
|
|
79
|
+
assert.not(hasESMSyntax(code), `CJS example ${index + 1} should not be detected as ESM: "${code}"`)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
NO_SYNTAX_EXAMPLES.forEach((code, index) => {
|
|
83
|
+
assert.not(hasESMSyntax(code), `No syntax example ${index + 1} should not be detected as ESM: "${code}"`)
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('hasCJSSyntax should detect CJS syntax correctly', () => {
|
|
88
|
+
CJS_EXAMPLES.forEach((code, index) => {
|
|
89
|
+
assert.ok(hasCJSSyntax(code), `CJS example ${index + 1} should be detected: "${code}"`)
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('hasCJSSyntax should not detect non-CJS syntax', () => {
|
|
94
|
+
ESM_EXAMPLES.forEach((code, index) => {
|
|
95
|
+
assert.not(hasCJSSyntax(code), `ESM example ${index + 1} should not be detected as CJS: "${code}"`)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
NO_SYNTAX_EXAMPLES.forEach((code, index) => {
|
|
99
|
+
assert.not(hasCJSSyntax(code), `No syntax example ${index + 1} should not be detected as CJS: "${code}"`)
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('detectSyntax should correctly identify ESM-only code', () => {
|
|
104
|
+
ESM_EXAMPLES.forEach((code, index) => {
|
|
105
|
+
const result = detectSyntax(code)
|
|
106
|
+
assert.ok(result.hasESM, `ESM example ${index + 1} should have hasESM=true: "${code}"`)
|
|
107
|
+
assert.not(result.hasCJS, `ESM example ${index + 1} should have hasCJS=false: "${code}"`)
|
|
108
|
+
assert.not(result.isMixed, `ESM example ${index + 1} should have isMixed=false: "${code}"`)
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test('detectSyntax should correctly identify CJS-only code', () => {
|
|
113
|
+
CJS_EXAMPLES.forEach((code, index) => {
|
|
114
|
+
const result = detectSyntax(code)
|
|
115
|
+
assert.not(result.hasESM, `CJS example ${index + 1} should have hasESM=false: "${code}"`)
|
|
116
|
+
assert.ok(result.hasCJS, `CJS example ${index + 1} should have hasCJS=true: "${code}"`)
|
|
117
|
+
assert.not(result.isMixed, `CJS example ${index + 1} should have isMixed=false: "${code}"`)
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test('detectSyntax should correctly identify mixed syntax code', () => {
|
|
122
|
+
MIXED_EXAMPLES.forEach((code, index) => {
|
|
123
|
+
const result = detectSyntax(code)
|
|
124
|
+
/*
|
|
125
|
+
console.info(code);
|
|
126
|
+
console.log(result)
|
|
127
|
+
/** */
|
|
128
|
+
assert.ok(result.hasESM, `Mixed example ${index + 1} should have hasESM=true: "${code}"`)
|
|
129
|
+
assert.ok(result.hasCJS, `Mixed example ${index + 1} should have hasCJS=true: "${code}"`)
|
|
130
|
+
assert.ok(result.isMixed, `Mixed example ${index + 1} should have isMixed=true: "${code}"`)
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test('detectSyntax should correctly identify code with no syntax', () => {
|
|
135
|
+
NO_SYNTAX_EXAMPLES.forEach((code, index) => {
|
|
136
|
+
const result = detectSyntax(code)
|
|
137
|
+
assert.not(result.hasESM, `No syntax example ${index + 1} should have hasESM=false: "${code}"`)
|
|
138
|
+
assert.not(result.hasCJS, `No syntax example ${index + 1} should have hasCJS=false: "${code}"`)
|
|
139
|
+
assert.not(result.isMixed, `No syntax example ${index + 1} should have isMixed=false: "${code}"`)
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test('should handle comments correctly (comments are always stripped)', () => {
|
|
144
|
+
COMMENTED_EXAMPLES.forEach((code, index) => {
|
|
145
|
+
const result = detectSyntax(code)
|
|
146
|
+
// Comments are always stripped to avoid false positives from commented syntax
|
|
147
|
+
assert.not(result.hasESM, `Commented example ${index + 1} should not detect commented ESM: "${code}"`)
|
|
148
|
+
assert.not(result.hasCJS, `Commented example ${index + 1} should not detect commented CJS: "${code}"`)
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
test('should detect actual syntax even when comments are present', () => {
|
|
153
|
+
COMMENTED_WITH_SYNTAX_EXAMPLES.forEach((code, index) => {
|
|
154
|
+
const result = detectSyntax(code)
|
|
155
|
+
// After stripping comments, actual syntax should be detected
|
|
156
|
+
if (code.includes('import')) {
|
|
157
|
+
assert.ok(result.hasESM, `Should detect ESM after stripping comments: "${code}"`)
|
|
158
|
+
}
|
|
159
|
+
if (code.includes('module.exports')) {
|
|
160
|
+
assert.ok(result.hasCJS, `Should detect CJS after stripping comments: "${code}"`)
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
const commentedCode = '// import foo from "bar"\nmodule.exports = foo'
|
|
165
|
+
const result = detectSyntax(commentedCode)
|
|
166
|
+
|
|
167
|
+
// Should detect the actual syntax, not the commented syntax
|
|
168
|
+
assert.not(result.hasESM, 'Should not detect commented ESM')
|
|
169
|
+
assert.ok(result.hasCJS, 'Should detect actual CJS syntax')
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
test('should handle complex real-world examples', () => {
|
|
173
|
+
const complexESM = `
|
|
174
|
+
import React from 'react'
|
|
175
|
+
import { useState, useEffect } from 'react'
|
|
176
|
+
import * as utils from './utils'
|
|
177
|
+
|
|
178
|
+
export default function App() {
|
|
179
|
+
const [state, setState] = useState()
|
|
180
|
+
return <div>Hello</div>
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export const helper = () => {}
|
|
184
|
+
`
|
|
185
|
+
|
|
186
|
+
const complexCJS = `
|
|
187
|
+
const express = require('express')
|
|
188
|
+
const path = require('path')
|
|
189
|
+
|
|
190
|
+
module.exports = function(app) {
|
|
191
|
+
app.get('/', (req, res) => {
|
|
192
|
+
res.send('Hello')
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
exports.helper = function() {}
|
|
197
|
+
`
|
|
198
|
+
|
|
199
|
+
const complexMixed = `
|
|
200
|
+
import React from 'react'
|
|
201
|
+
const express = require('express')
|
|
202
|
+
|
|
203
|
+
export default function App() {
|
|
204
|
+
return <div>Hello</div>
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
module.exports = { App }
|
|
208
|
+
`
|
|
209
|
+
|
|
210
|
+
// Test ESM
|
|
211
|
+
const esmResult = detectSyntax(complexESM)
|
|
212
|
+
assert.ok(esmResult.hasESM, 'Complex ESM should be detected')
|
|
213
|
+
assert.not(esmResult.hasCJS, 'Complex ESM should not have CJS')
|
|
214
|
+
assert.not(esmResult.isMixed, 'Complex ESM should not be mixed')
|
|
215
|
+
|
|
216
|
+
// Test CJS
|
|
217
|
+
const cjsResult = detectSyntax(complexCJS)
|
|
218
|
+
assert.not(cjsResult.hasESM, 'Complex CJS should not have ESM')
|
|
219
|
+
assert.ok(cjsResult.hasCJS, 'Complex CJS should be detected')
|
|
220
|
+
assert.not(cjsResult.isMixed, 'Complex CJS should not be mixed')
|
|
221
|
+
|
|
222
|
+
// Test Mixed
|
|
223
|
+
const mixedResult = detectSyntax(complexMixed)
|
|
224
|
+
assert.ok(mixedResult.hasESM, 'Complex mixed should have ESM')
|
|
225
|
+
assert.ok(mixedResult.hasCJS, 'Complex mixed should have CJS')
|
|
226
|
+
assert.ok(mixedResult.isMixed, 'Complex mixed should be mixed')
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
test('should handle edge cases and whitespace', () => {
|
|
230
|
+
// Empty string
|
|
231
|
+
const emptyResult = detectSyntax('')
|
|
232
|
+
assert.not(emptyResult.hasESM, 'Empty string should not have ESM')
|
|
233
|
+
assert.not(emptyResult.hasCJS, 'Empty string should not have CJS')
|
|
234
|
+
assert.not(emptyResult.isMixed, 'Empty string should not be mixed')
|
|
235
|
+
|
|
236
|
+
// Whitespace only
|
|
237
|
+
const whitespaceResult = detectSyntax(' \n\t ')
|
|
238
|
+
assert.not(whitespaceResult.hasESM, 'Whitespace only should not have ESM')
|
|
239
|
+
assert.not(whitespaceResult.hasCJS, 'Whitespace only should not have CJS')
|
|
240
|
+
assert.not(whitespaceResult.isMixed, 'Whitespace only should not be mixed')
|
|
241
|
+
|
|
242
|
+
// Syntax with extra whitespace
|
|
243
|
+
const spacedESM = ' import foo from "bar" '
|
|
244
|
+
const spacedCJS = ' module.exports = foo '
|
|
245
|
+
|
|
246
|
+
assert.ok(hasESMSyntax(spacedESM), 'ESM with extra whitespace should be detected')
|
|
247
|
+
assert.ok(hasCJSSyntax(spacedCJS), 'CJS with extra whitespace should be detected')
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
test('should handle regex edge cases', () => {
|
|
251
|
+
// Test that the regex doesn't match partial words
|
|
252
|
+
const partialESM = 'importing foo from "bar"'
|
|
253
|
+
const partialCJS = 'module.exporting = foo'
|
|
254
|
+
|
|
255
|
+
assert.not(hasESMSyntax(partialESM), 'Partial ESM word should not be detected')
|
|
256
|
+
assert.not(hasCJSSyntax(partialCJS), 'Partial CJS word should not be detected')
|
|
257
|
+
|
|
258
|
+
// Test that it matches at word boundaries
|
|
259
|
+
const boundaryESM = 'const x = "import foo from bar"'
|
|
260
|
+
const boundaryCJS = 'const x = "module.exports = foo"'
|
|
261
|
+
|
|
262
|
+
assert.not(hasESMSyntax(boundaryESM), 'ESM in string should not be detected')
|
|
263
|
+
assert.not(hasCJSSyntax(boundaryCJS), 'CJS in string should not be detected')
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
test('should handle TypeScript-specific syntax', () => {
|
|
267
|
+
const tsExamples = [
|
|
268
|
+
'import type { Foo } from "bar"',
|
|
269
|
+
'export type Foo = string',
|
|
270
|
+
'import { type Foo } from "bar"',
|
|
271
|
+
'export interface Foo {}',
|
|
272
|
+
'export enum Foo {}'
|
|
273
|
+
]
|
|
274
|
+
|
|
275
|
+
tsExamples.forEach((code, index) => {
|
|
276
|
+
assert.ok(hasESMSyntax(code), `TypeScript example ${index + 1} should be detected as ESM: "${code}"`)
|
|
277
|
+
})
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
test.run()
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
const { test } = require('uvu')
|
|
2
|
+
const assert = require('uvu/assert')
|
|
3
|
+
const {
|
|
4
|
+
getExports,
|
|
5
|
+
getDefaultExports,
|
|
6
|
+
getNamedExports,
|
|
7
|
+
getNamedDeclarationExports,
|
|
8
|
+
getAllExports,
|
|
9
|
+
getNamespaceExports,
|
|
10
|
+
getTypeExports,
|
|
11
|
+
getAssertionExports
|
|
12
|
+
} = require('../src/get-exports.js')
|
|
13
|
+
|
|
14
|
+
test('should extract default exports', () => {
|
|
15
|
+
const code = `
|
|
16
|
+
export default function myFunction() {}
|
|
17
|
+
export default class MyClass {}
|
|
18
|
+
export default { foo: 'bar' }
|
|
19
|
+
export default [1, 2, 3]
|
|
20
|
+
export default 'hello'
|
|
21
|
+
export default 42
|
|
22
|
+
export default () => {}
|
|
23
|
+
`
|
|
24
|
+
|
|
25
|
+
const exports = getDefaultExports(code)
|
|
26
|
+
|
|
27
|
+
assert.ok(exports.length === 6, 'Should find 6 default exports')
|
|
28
|
+
|
|
29
|
+
// Check function export
|
|
30
|
+
const functionExport = exports.find(exp => exp.exportName === 'myFunction')
|
|
31
|
+
assert.ok(functionExport, 'Should find function export')
|
|
32
|
+
assert.equal(functionExport.exportType, 'default')
|
|
33
|
+
assert.equal(functionExport.exportValue, 'function myFunction')
|
|
34
|
+
|
|
35
|
+
// Check class export
|
|
36
|
+
const classExport = exports.find(exp => exp.exportName === 'MyClass')
|
|
37
|
+
assert.ok(classExport, 'Should find class export')
|
|
38
|
+
assert.equal(classExport.exportType, 'default')
|
|
39
|
+
assert.equal(classExport.exportValue, 'class MyClass')
|
|
40
|
+
|
|
41
|
+
// Check object literal
|
|
42
|
+
const objectExport = exports.find(exp => exp.exportName === 'literal')
|
|
43
|
+
assert.ok(objectExport, 'Should find object literal export')
|
|
44
|
+
assert.equal(objectExport.exportValue, "{ foo: 'bar' }")
|
|
45
|
+
|
|
46
|
+
// Check array literal
|
|
47
|
+
const arrayExport = exports.find(exp => exp.exportValue === '[1, 2, 3]')
|
|
48
|
+
assert.ok(arrayExport, 'Should find array literal export')
|
|
49
|
+
assert.equal(arrayExport.exportName, 'literal')
|
|
50
|
+
|
|
51
|
+
// Check number literal
|
|
52
|
+
const numberExport = exports.find(exp => exp.exportValue === '42')
|
|
53
|
+
assert.ok(numberExport, 'Should find number literal export')
|
|
54
|
+
assert.equal(numberExport.exportName, '42')
|
|
55
|
+
|
|
56
|
+
// Check arrow function
|
|
57
|
+
const arrowExport = exports.find(exp => exp.exportValue === '() =>')
|
|
58
|
+
assert.ok(arrowExport, 'Should find arrow function export')
|
|
59
|
+
assert.equal(arrowExport.exportName, 'anonymous')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('should extract named exports', () => {
|
|
63
|
+
const code = `
|
|
64
|
+
export const myVariable = 'value'
|
|
65
|
+
export let anotherVariable = 'value'
|
|
66
|
+
export var thirdVariable = 'value'
|
|
67
|
+
export function myFunction() {}
|
|
68
|
+
export class MyClass {}
|
|
69
|
+
export async function asyncFunction() {}
|
|
70
|
+
`
|
|
71
|
+
|
|
72
|
+
const exports = getNamedExports(code)
|
|
73
|
+
|
|
74
|
+
assert.ok(exports.length === 6, 'Should find 6 named exports')
|
|
75
|
+
|
|
76
|
+
const exportNames = exports.map(exp => exp.exportName)
|
|
77
|
+
assert.ok(exportNames.includes('myVariable'), 'Should find const export')
|
|
78
|
+
assert.ok(exportNames.includes('anotherVariable'), 'Should find let export')
|
|
79
|
+
assert.ok(exportNames.includes('thirdVariable'), 'Should find var export')
|
|
80
|
+
assert.ok(exportNames.includes('myFunction'), 'Should find function export')
|
|
81
|
+
assert.ok(exportNames.includes('MyClass'), 'Should find class export')
|
|
82
|
+
assert.ok(exportNames.includes('asyncFunction'), 'Should find async function export')
|
|
83
|
+
|
|
84
|
+
exports.forEach(exp => {
|
|
85
|
+
assert.equal(exp.exportType, 'named')
|
|
86
|
+
assert.ok(exp.isExport)
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('should extract named declaration exports', () => {
|
|
91
|
+
const code = `
|
|
92
|
+
export { myFunction, anotherFunction }
|
|
93
|
+
export { myFunction as aliasedFunction, anotherFunction }
|
|
94
|
+
export { default } from './module'
|
|
95
|
+
export { default as aliasedDefault } from './module'
|
|
96
|
+
export { Component, Button } from './components'
|
|
97
|
+
`
|
|
98
|
+
|
|
99
|
+
const exports = getNamedDeclarationExports(code)
|
|
100
|
+
|
|
101
|
+
assert.ok(exports.length === 8, 'Should find 8 named declaration exports')
|
|
102
|
+
|
|
103
|
+
// Check simple exports
|
|
104
|
+
const simpleExports = exports.filter(exp => !exp.exportAlias)
|
|
105
|
+
assert.ok(simpleExports.length === 6, 'Should have 6 simple exports')
|
|
106
|
+
|
|
107
|
+
// Check aliased exports
|
|
108
|
+
const aliasedExports = exports.filter(exp => exp.exportAlias)
|
|
109
|
+
assert.ok(aliasedExports.length === 2, 'Should have 2 aliased exports')
|
|
110
|
+
|
|
111
|
+
// Check specific aliases
|
|
112
|
+
const aliasedFunction = exports.find(exp => exp.exportAlias === 'aliasedFunction')
|
|
113
|
+
assert.ok(aliasedFunction, 'Should find aliased function export')
|
|
114
|
+
assert.equal(aliasedFunction.exportName, 'myFunction')
|
|
115
|
+
|
|
116
|
+
const aliasedDefault = exports.find(exp => exp.exportAlias === 'aliasedDefault')
|
|
117
|
+
assert.ok(aliasedDefault, 'Should find aliased default export')
|
|
118
|
+
assert.equal(aliasedDefault.exportName, 'default')
|
|
119
|
+
|
|
120
|
+
exports.forEach(exp => {
|
|
121
|
+
assert.equal(exp.exportType, 'named')
|
|
122
|
+
assert.ok(exp.isExport)
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test('should extract all exports (re-exports)', () => {
|
|
127
|
+
const code = `
|
|
128
|
+
export * from './module'
|
|
129
|
+
export * from './utils'
|
|
130
|
+
export * from '@angular/core'
|
|
131
|
+
`
|
|
132
|
+
|
|
133
|
+
const exports = getAllExports(code)
|
|
134
|
+
|
|
135
|
+
assert.ok(exports.length === 3, 'Should find 3 all exports')
|
|
136
|
+
|
|
137
|
+
const fromModules = exports.map(exp => exp.from)
|
|
138
|
+
assert.ok(fromModules.includes('./module'), 'Should export all from ./module')
|
|
139
|
+
assert.ok(fromModules.includes('./utils'), 'Should export all from ./utils')
|
|
140
|
+
assert.ok(fromModules.includes('@angular/core'), 'Should export all from @angular/core')
|
|
141
|
+
|
|
142
|
+
exports.forEach(exp => {
|
|
143
|
+
assert.equal(exp.exportType, 'all')
|
|
144
|
+
assert.ok(exp.isExport)
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('should extract namespace exports', () => {
|
|
149
|
+
const code = `
|
|
150
|
+
export * as namespace from './module'
|
|
151
|
+
export * as utils from './utils'
|
|
152
|
+
export * as angular from '@angular/core'
|
|
153
|
+
`
|
|
154
|
+
|
|
155
|
+
const exports = getNamespaceExports(code)
|
|
156
|
+
|
|
157
|
+
assert.ok(exports.length === 3, 'Should find 3 namespace exports')
|
|
158
|
+
|
|
159
|
+
const namespaceNames = exports.map(exp => exp.namespace)
|
|
160
|
+
assert.ok(namespaceNames.includes('namespace'), 'Should find namespace export')
|
|
161
|
+
assert.ok(namespaceNames.includes('utils'), 'Should find utils namespace export')
|
|
162
|
+
assert.ok(namespaceNames.includes('angular'), 'Should find angular namespace export')
|
|
163
|
+
|
|
164
|
+
const fromModules = exports.map(exp => exp.from)
|
|
165
|
+
assert.ok(fromModules.includes('./module'), 'Should export namespace from ./module')
|
|
166
|
+
assert.ok(fromModules.includes('./utils'), 'Should export namespace from ./utils')
|
|
167
|
+
assert.ok(fromModules.includes('@angular/core'), 'Should export namespace from @angular/core')
|
|
168
|
+
|
|
169
|
+
exports.forEach(exp => {
|
|
170
|
+
assert.equal(exp.exportType, 'namespace')
|
|
171
|
+
assert.ok(exp.isExport)
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
test('should extract type exports', () => {
|
|
176
|
+
const code = `
|
|
177
|
+
export type MyType = string
|
|
178
|
+
export interface MyInterface {}
|
|
179
|
+
export type User = { id: string; name: string }
|
|
180
|
+
export interface ComponentProps { children: React.ReactNode }
|
|
181
|
+
`
|
|
182
|
+
|
|
183
|
+
const exports = getTypeExports(code)
|
|
184
|
+
|
|
185
|
+
assert.ok(exports.length === 4, 'Should find 4 type exports')
|
|
186
|
+
|
|
187
|
+
const typeNames = exports.map(exp => exp.exportName)
|
|
188
|
+
assert.ok(typeNames.includes('MyType'), 'Should find MyType export')
|
|
189
|
+
assert.ok(typeNames.includes('MyInterface'), 'Should find MyInterface export')
|
|
190
|
+
assert.ok(typeNames.includes('User'), 'Should find User type export')
|
|
191
|
+
assert.ok(typeNames.includes('ComponentProps'), 'Should find ComponentProps interface export')
|
|
192
|
+
|
|
193
|
+
exports.forEach(exp => {
|
|
194
|
+
assert.equal(exp.exportType, 'type')
|
|
195
|
+
assert.ok(exp.isExport)
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
test('should extract assertion exports', () => {
|
|
200
|
+
const code = `
|
|
201
|
+
export { default } from './module' assert { type: 'json' }
|
|
202
|
+
export { Component } from './component' with { type: 'css' }
|
|
203
|
+
export { data } from './data.json' assert { type: 'json' }
|
|
204
|
+
`
|
|
205
|
+
|
|
206
|
+
const exports = getAssertionExports(code)
|
|
207
|
+
|
|
208
|
+
assert.ok(exports.length === 3, 'Should find 3 assertion exports')
|
|
209
|
+
|
|
210
|
+
// Check JSON assertion
|
|
211
|
+
const jsonExports = exports.filter(exp => exp.assertion.includes('json'))
|
|
212
|
+
assert.ok(jsonExports.length === 2, 'Should find 2 JSON assertion exports')
|
|
213
|
+
|
|
214
|
+
// Check CSS assertion
|
|
215
|
+
const cssExports = exports.filter(exp => exp.assertion.includes('css'))
|
|
216
|
+
assert.ok(cssExports.length === 1, 'Should find 1 CSS assertion export')
|
|
217
|
+
|
|
218
|
+
// Check assertion types
|
|
219
|
+
const assertExports = exports.filter(exp => exp.assertionType === 'assert')
|
|
220
|
+
const withExports = exports.filter(exp => exp.assertionType === 'with')
|
|
221
|
+
assert.ok(assertExports.length === 2, 'Should find 2 assert exports')
|
|
222
|
+
assert.ok(withExports.length === 1, 'Should find 1 with export')
|
|
223
|
+
|
|
224
|
+
exports.forEach(exp => {
|
|
225
|
+
assert.equal(exp.exportType, 'assertion')
|
|
226
|
+
assert.ok(exp.isExport)
|
|
227
|
+
assert.ok(exp.isAssertion)
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
test('should combine all export types in getExports', () => {
|
|
232
|
+
const code = `
|
|
233
|
+
// Default exports
|
|
234
|
+
export default function myFunction() {}
|
|
235
|
+
export default class MyClass {}
|
|
236
|
+
|
|
237
|
+
// Named exports
|
|
238
|
+
export const myVariable = 'value'
|
|
239
|
+
export function myFunction() {}
|
|
240
|
+
|
|
241
|
+
// Named declaration exports
|
|
242
|
+
export { myFunction, anotherFunction }
|
|
243
|
+
export { myFunction as aliasedFunction }
|
|
244
|
+
|
|
245
|
+
// Re-exports
|
|
246
|
+
export * from './module'
|
|
247
|
+
export * as namespace from './module'
|
|
248
|
+
|
|
249
|
+
// Type exports
|
|
250
|
+
export type MyType = string
|
|
251
|
+
export interface MyInterface {}
|
|
252
|
+
`
|
|
253
|
+
|
|
254
|
+
const exports = getExports(code)
|
|
255
|
+
|
|
256
|
+
assert.ok(exports.length === 11, 'Should find 11 total exports')
|
|
257
|
+
|
|
258
|
+
const exportTypes = exports.map(exp => exp.exportType)
|
|
259
|
+
const defaultCount = exportTypes.filter(type => type === 'default').length
|
|
260
|
+
const namedCount = exportTypes.filter(type => type === 'named').length
|
|
261
|
+
const allCount = exportTypes.filter(type => type === 'all').length
|
|
262
|
+
const namespaceCount = exportTypes.filter(type => type === 'namespace').length
|
|
263
|
+
const typeCount = exportTypes.filter(type => type === 'type').length
|
|
264
|
+
|
|
265
|
+
assert.equal(defaultCount, 2, 'Should have 2 default exports')
|
|
266
|
+
assert.equal(namedCount, 5, 'Should have 5 named exports')
|
|
267
|
+
assert.equal(allCount, 1, 'Should have 1 all export')
|
|
268
|
+
assert.equal(namespaceCount, 1, 'Should have 1 namespace export')
|
|
269
|
+
assert.equal(typeCount, 2, 'Should have 2 type exports')
|
|
270
|
+
|
|
271
|
+
// Check sorting by index
|
|
272
|
+
for (let i = 1; i < exports.length; i++) {
|
|
273
|
+
assert.ok(exports[i].index >= exports[i-1].index, 'Exports should be sorted by index')
|
|
274
|
+
}
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
test('should handle empty code', () => {
|
|
278
|
+
const exports = getExports('')
|
|
279
|
+
|
|
280
|
+
assert.ok(exports.length === 0, 'Should return empty array for empty code')
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
test('should handle code with no exports', () => {
|
|
284
|
+
const code = `
|
|
285
|
+
function myFunction() {}
|
|
286
|
+
const myVariable = 'value'
|
|
287
|
+
import { something } from './module'
|
|
288
|
+
`
|
|
289
|
+
|
|
290
|
+
const exports = getExports(code)
|
|
291
|
+
|
|
292
|
+
assert.ok(exports.length === 0, 'Should return empty array for code with no exports')
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
test('should handle complex export patterns', () => {
|
|
296
|
+
const code = `
|
|
297
|
+
// Complex default exports
|
|
298
|
+
export default (props) => <Component {...props} />
|
|
299
|
+
export default function() { return 'anonymous' }
|
|
300
|
+
export default class { constructor() {} }
|
|
301
|
+
|
|
302
|
+
// Complex named exports with destructuring
|
|
303
|
+
export const { Component, Button } = React
|
|
304
|
+
export const [first, second] = ['a', 'b']
|
|
305
|
+
|
|
306
|
+
// Complex re-exports
|
|
307
|
+
export { default as MyComponent, Button, Input } from './components'
|
|
308
|
+
export {
|
|
309
|
+
default as App,
|
|
310
|
+
Header,
|
|
311
|
+
Footer
|
|
312
|
+
} from './app'
|
|
313
|
+
`
|
|
314
|
+
|
|
315
|
+
const exports = getExports(code)
|
|
316
|
+
|
|
317
|
+
assert.ok(exports.length > 0, 'Should find exports in complex code')
|
|
318
|
+
|
|
319
|
+
// Should handle anonymous functions and classes
|
|
320
|
+
const anonymousExports = exports.filter(exp => exp.exportName === 'anonymous')
|
|
321
|
+
assert.ok(anonymousExports.length >= 2, 'Should find anonymous function and class exports')
|
|
322
|
+
|
|
323
|
+
// Should handle complex named declaration exports
|
|
324
|
+
const namedDeclarations = exports.filter(exp => exp.exportType === 'named')
|
|
325
|
+
assert.ok(namedDeclarations.length >= 6, 'Should find complex named declaration exports')
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
test('should handle assertion exports with options', () => {
|
|
329
|
+
const code = `
|
|
330
|
+
export { default } from './module' assert { type: 'json' }
|
|
331
|
+
export { Component } from './component' with { type: 'css' }
|
|
332
|
+
`
|
|
333
|
+
|
|
334
|
+
const exports = getExports(code, { includeAssertionExports: true })
|
|
335
|
+
|
|
336
|
+
assert.ok(exports.length === 4, 'Should find 4 exports when assertions enabled')
|
|
337
|
+
|
|
338
|
+
const assertionExports = exports.filter(exp => exp.isAssertion)
|
|
339
|
+
assert.ok(assertionExports.length === 2, 'Should find 2 assertion exports')
|
|
340
|
+
|
|
341
|
+
// Test without assertion exports
|
|
342
|
+
const exportsWithoutAssertions = getExports(code, { includeAssertionExports: false })
|
|
343
|
+
assert.ok(exportsWithoutAssertions.length === 2, 'Should find 2 named exports when assertions disabled')
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
test.run()
|