Skip to content

Commit

Permalink
feat(utils): hasModuleId
Browse files Browse the repository at this point in the history
Signed-off-by: Lexus Drumgold <unicornware@flexdevelopment.llc>
  • Loading branch information
unicornware committed Feb 28, 2024
1 parent ff3aa94 commit 9ab9994
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 2 deletions.
1 change: 1 addition & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,4 @@ ignore:
profiling:
critical_files_paths:
- src/utils/get-identifiers.ts
- src/utils/has-module-id.ts
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@
"@flex-development/tutils": "6.0.0-alpha.25",
"@rollup/pluginutils": "5.1.0",
"devlop": "1.1.0",
"estree-util-visit": "2.0.0"
"estree-util-visit": "2.0.0",
"unist-util-is": "6.0.0"
},
"devDependencies": {
"@arethetypeswrong/cli": "0.14.1",
Expand Down
126 changes: 126 additions & 0 deletions src/utils/__tests__/has-module-id.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/**
* @file Unit Tests - hasModuleId
* @module estree-util-unassert/utils/tests/unit/hasModuleId
*/

import type { Assign } from '@flex-development/tutils'
import { createFilter, type CreateFilter } from '@rollup/pluginutils'
import type {
AwaitExpression,
CallExpression,
Identifier,
ImportDeclaration,
Literal
} from 'estree'
import testSubject from '../has-module-id'

describe('unit:utils/hasModuleId', () => {
let devlopIdentifier: Identifier
let devlopLiteral: Assign<Literal, { raw: string; value: string }>
let filter: ReturnType<CreateFilter>

beforeAll(() => {
devlopLiteral = { raw: '\'devlop\'', type: 'Literal', value: 'devlop' }
devlopIdentifier = { name: devlopLiteral.value, type: 'Identifier' }
filter = createFilter(/^devlop$/, [], { resolve: false })
})

it('should return false if node.type is ignored', () => {
expect(testSubject(devlopIdentifier, filter)).to.be.false
})

describe('AwaitExpression', () => {
it('should return false if node is not dynamic assertion import', () => {
// Arrange
const node: AwaitExpression = {
argument: { source: devlopIdentifier, type: 'ImportExpression' },
type: 'AwaitExpression'
}

// Act + Expect
expect(testSubject(node, filter)).to.be.false
})

it('should return true node is dynamic assertion import', () => {
// Arrange
const node: AwaitExpression = {
argument: { source: devlopLiteral, type: 'ImportExpression' },
type: 'AwaitExpression'
}

// Act + Expect
expect(testSubject(node, filter)).to.be.true
})
})

describe('CallExpression', () => {
it('should return false if node is not assertion `require` call', () => {
// Arrange
const node: CallExpression = {
arguments: [devlopIdentifier],
callee: { name: 'require', type: 'Identifier' },
optional: false,
type: 'CallExpression'
}

// Act + Expect
expect(testSubject(node, filter)).to.be.false
})

it('should return true node is assertion `require` call', () => {
// Arrange
const node: CallExpression = {
arguments: [devlopLiteral],
callee: { name: 'require', type: 'Identifier' },
optional: false,
type: 'CallExpression'
}

// Act + Expect
expect(testSubject(node, filter)).to.be.true
})
})

describe('ImportDeclaration', () => {
let invariant: Assign<Literal, { raw: string; value: string }>

beforeAll(() => {
invariant = { raw: '\'invariant\'', type: 'Literal', value: 'invariant' }
})

it('should return false if node is not assertion import', () => {
// Arrange
const node: ImportDeclaration = {
source: invariant,
specifiers: [
{
local: { name: invariant.value, type: 'Identifier' },
type: 'ImportDefaultSpecifier'
}
],
type: 'ImportDeclaration'
}

// Act + Expect
expect(testSubject(node, filter)).to.be.false
})

it('should return true node is assertion import', () => {
// Arrange
const node: ImportDeclaration = {
source: devlopLiteral,
specifiers: [
{
imported: { name: 'ok', type: 'Identifier' },
local: { name: 'ok', type: 'Identifier' },
type: 'ImportSpecifier'
}
],
type: 'ImportDeclaration'
}

// Act + Expect
expect(testSubject(node, filter)).to.be.true
})
})
})
58 changes: 58 additions & 0 deletions src/utils/has-module-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* @file Utilities - hasModuleId
* @module estree-util-unassert/utils/hasModuleId
*/

import type { Nilable } from '@flex-development/tutils'
import type {
AwaitExpression,
CallExpression,
ImportDeclaration,
Node
} from 'estree'
import { is } from 'unist-util-is'

/**
* Nodes that can contain assertion module ids.
*
* @internal
*/
type HasModuleId = AwaitExpression | CallExpression | ImportDeclaration

/**
* Check if `node` contains an assertion module id.
*
* @see {@linkcode HasModuleId}
* @see {@linkcode Node}
*
* @param {Nilable<Node>} node - Node to check
* @param {(id: unknown) => boolean} filter - Module id filter
* @return {node is HasModuleId} `true` if `node` contains assertion module id
*/
const hasModuleId = (
node: Nilable<Node>,
filter: (id: unknown) => boolean
): node is HasModuleId => {
switch (true) {
case is(node, 'AwaitExpression'):
return (
is(node.argument, 'ImportExpression') &&
is(node.argument.source, 'Literal') &&
filter(node.argument.source.value)
)
case is(node, 'CallExpression'):
return (
is(node.callee, 'Identifier') &&
node.callee.name === 'require' &&
!!node.arguments.length &&
is(node.arguments[0], 'Literal') &&
filter(node.arguments[0].value)
)
case is(node, 'ImportDeclaration'):
return is(node.source, 'Literal') && filter(node.source.value)
default:
return false
}
}

export default hasModuleId
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
*/

export { default as getIdentifiers } from './get-identifiers'
export { default as hasModuleId } from './has-module-id'
export { default as MODULES_REGEX } from './modules-regex'
3 changes: 2 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,7 @@ __metadata:
trash-cli: "npm:5.0.0"
ts-dedent: "npm:2.2.0"
typescript: "npm:5.3.3"
unist-util-is: "npm:6.0.0"
vite-tsconfig-paths: "npm:4.3.1"
vitest: "npm:1.3.1"
yaml-eslint-parser: "npm:1.2.2"
Expand Down Expand Up @@ -10410,7 +10411,7 @@ __metadata:
languageName: node
linkType: hard

"unist-util-is@npm:^6.0.0":
"unist-util-is@npm:6.0.0, unist-util-is@npm:^6.0.0":
version: 6.0.0
resolution: "unist-util-is@npm:6.0.0"
dependencies:
Expand Down

0 comments on commit 9ab9994

Please sign in to comment.