-
Notifications
You must be signed in to change notification settings - Fork 0
/
remove-vue-import.ts
123 lines (113 loc) · 4.34 KB
/
remove-vue-import.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import { LogContexts, LogLevels } from 'bs-logger'
import type { ConfigSet } from 'ts-jest/dist/config/config-set';
import type * as _ts from 'typescript'
// this is a unique identifier for your transformer. `ts-jest` uses this for jest cache key
export const name = 'remove-vue-import'
// increment this each time you change the behavior of your transformer. `ts-jest` uses this for jest cache key
export const version = 1
const VUE_GLOBAL_NAME = 'vue'
const ROOT_LEVEL_AST = 1
/**
* @usage In jest config, define as
* // jest.config.js
* module.exports = {
* // other configs
* globals: {
* 'ts-jest': {
* astTransformers: {
* after: ['vue-ts-jest/dist/transformers/remove-vue-import']
* }
* }
* }
* }
*/
export function factory(cs: ConfigSet): (ctx: _ts.TransformationContext) => _ts.Transformer<_ts.SourceFile> {
const logger = cs.logger.child({ namespace: 'remove-vue-import' })
const ts = cs.compilerModule
const importNames: Set<string> = new Set<string>();
const isRequire = (node: _ts.Node): node is _ts.CallExpression =>
ts.isCallExpression(node) &&
ts.isIdentifier(node.expression) &&
node.expression.text === 'require' &&
ts.isStringLiteral(node.arguments[0]) &&
node.arguments.length === 1
const isVueRequireStmt = (node: _ts.Node) => isRequire(node)
&& (node.arguments[0] as _ts.StringLiteral).text === VUE_GLOBAL_NAME
const isVueNamespaceImport = (node: _ts.Node): node is _ts.ImportDeclaration => ts.isImportDeclaration(node)
&& ts.isStringLiteral(node.moduleSpecifier)
&& node.moduleSpecifier.text === VUE_GLOBAL_NAME
const createVisitor = (ctx: _ts.TransformationContext, _sf: _ts.SourceFile) => {
/**
* Current block level
*/
let level = 0
/**
* List of nodes which needs to be deleted, indexed by their owning level
*/
const removingImportNodes: _ts.Statement[][] = []
/**
* Called when we enter a block to increase the level
*/
const enter = () => {
level++
// reuse arrays
if (removingImportNodes[level]) {
removingImportNodes[level].splice(0, removingImportNodes[level].length)
}
}
/**
* Called when we leave a block to decrease the level
*/
const exit = () => level--
const visitor: _ts.Visitor = node => {
// enter this level
enter()
// visit each child
let resultNode = ts.visitEachChild(node, visitor, ctx)
// check if we have something to delete in this level
if (removingImportNodes[level]?.length) {
const newNode = ts.getMutableClone(resultNode) as _ts.Block
const otherStmts = (resultNode as _ts.Block).statements.filter(
(s) => !removingImportNodes[level].includes(s) && !isVueRequireStmt(s) && !isVueNamespaceImport(s),
)
resultNode = {
...newNode,
statements: ts.createNodeArray([...otherStmts]),
} as _ts.Statement
}
if (ts.isCallExpression(resultNode)
&& ts.isPropertyAccessExpression(resultNode.expression)
&& ts.isIdentifier(resultNode.expression.expression) && importNames.has(resultNode.expression.expression.text)) {
const newNode = ts.getMutableClone(resultNode)
resultNode = {
...newNode,
expression: {
...newNode.expression,
expression: ts.createIdentifier('vue_1')
}
} as _ts.CallExpression
}
// exit the level
exit()
if ((isVueRequireStmt(resultNode) || isVueNamespaceImport(resultNode)) && level === ROOT_LEVEL_AST) {
if (isVueNamespaceImport(resultNode)
&& resultNode.importClause?.namedBindings
&& ts.isNamespaceImport(resultNode.importClause.namedBindings)
&& resultNode.importClause.namedBindings.name) {
importNames.add(resultNode.importClause.namedBindings.name.text)
}
removingImportNodes[ level ] = [ resultNode as _ts.Statement ]
}
// finally returns the currently visited node
return resultNode
}
return visitor
}
// returns the transformer factory
return (ctx: _ts.TransformationContext): _ts.Transformer<_ts.SourceFile> =>
logger.wrap(
{ [LogContexts.logLevel]: LogLevels.debug, call: null },
'visitSourceFileNode(): remove vue import',
(sf: _ts.SourceFile) => ts.visitNode(sf, createVisitor(ctx, sf)),
)
}