1
1
import { nodeModules } from './utils'
2
+ import * as semver from 'semver'
3
+ import { createLanguageService } from './dummyLanguageService'
2
4
3
- // uses at testing only
5
+ // used at testing only
4
6
declare const __TS_SEVER_PATH__ : string | undefined
5
7
6
- const getPatchedNavModule = ( ) => {
8
+ const getPatchedNavModule = ( ) : { getNavigationTree ( ...args ) } => {
9
+ // what is happening here: grabbing & patching NavigationBar module contents from actual running JS
7
10
const tsServerPath = typeof __TS_SEVER_PATH__ !== 'undefined' ? __TS_SEVER_PATH__ : require . main ! . filename
11
+ // current lib/tsserver.js
8
12
const mainScript = nodeModules ! . fs . readFileSync ( tsServerPath , 'utf8' ) as string
9
- const startIdx = mainScript . indexOf ( 'var NavigationBar;' )
10
- const ph = '(ts.NavigationBar = {}));'
11
- const lines = mainScript . slice ( startIdx , mainScript . indexOf ( ph ) + ph . length ) . split ( / \r ? \n / )
12
- const patchPlaces : {
13
- predicateString : string
13
+ type PatchData = {
14
+ markerModuleStart : string
15
+ skipStartMarker ?: boolean
16
+ markerModuleEnd : string /* | RegExp */
17
+ patches : PatchLocation [ ]
18
+ returnModuleCode : string
19
+ }
20
+ type PatchLocation = {
21
+ searchString : string
14
22
linesOffset : number
15
23
addString ?: string
16
24
removeLines ?: number
17
- } [ ] = [
25
+ }
26
+
27
+ const patchLocations : PatchLocation [ ] = [
18
28
{
19
- predicateString : 'function addChildrenRecursively(node)' ,
29
+ searchString : 'function addChildrenRecursively(node)' ,
20
30
linesOffset : 7 ,
21
31
addString : `
22
32
case ts.SyntaxKind.JsxSelfClosingElement:
@@ -29,7 +39,7 @@ const getPatchedNavModule = () => {
29
39
break` ,
30
40
} ,
31
41
{
32
- predicateString : 'return "<unknown>";' ,
42
+ searchString : 'return "<unknown>";' ,
33
43
linesOffset : - 1 ,
34
44
addString : `
35
45
case ts.SyntaxKind.JsxSelfClosingElement:
@@ -38,13 +48,57 @@ const getPatchedNavModule = () => {
38
48
return getNameFromJsxTag(node.openingElement);` ,
39
49
} ,
40
50
]
41
- for ( let { addString, linesOffset, predicateString, removeLines = 0 } of patchPlaces ) {
42
- const addTypeIndex = lines . findIndex ( line => line . includes ( predicateString ) )
51
+
52
+ // semver: can't use compare as it incorrectly works with build postfix
53
+ const isTs5 = semver . major ( ts . version ) >= 5
54
+ const {
55
+ markerModuleStart,
56
+ markerModuleEnd,
57
+ patches,
58
+ returnModuleCode,
59
+ skipStartMarker = false ,
60
+ } : PatchData = ! isTs5
61
+ ? {
62
+ markerModuleStart : 'var NavigationBar;' ,
63
+ markerModuleEnd : '(ts.NavigationBar = {}));' ,
64
+ patches : patchLocations ,
65
+ returnModuleCode : 'NavigationBar' ,
66
+ }
67
+ : {
68
+ markerModuleStart : '// src/services/navigationBar.ts' ,
69
+ skipStartMarker : true ,
70
+ markerModuleEnd : '// src/' ,
71
+ patches : patchLocations ,
72
+ returnModuleCode : '{ getNavigationTree }' ,
73
+ }
74
+
75
+ const contentAfterModuleStart = mainScript . slice ( mainScript . indexOf ( markerModuleStart ) + ( skipStartMarker ? markerModuleStart . length : 0 ) )
76
+ const lines = contentAfterModuleStart . slice ( 0 , contentAfterModuleStart . indexOf ( markerModuleEnd ) + markerModuleEnd . length ) . split ( / \r ? \n / )
77
+
78
+ for ( let { addString, linesOffset, searchString, removeLines = 0 } of patches ) {
79
+ const addTypeIndex = lines . findIndex ( line => line . includes ( searchString ) )
43
80
if ( addTypeIndex !== - 1 ) {
44
81
lines . splice ( addTypeIndex + linesOffset , removeLines , ...( addString ? [ addString ] : [ ] ) )
82
+ } else {
83
+ console . warn ( `TS Essentials: Failed to patch NavBar module (outline): ${ searchString } ` )
84
+ }
85
+ }
86
+ const getModuleString = ( ) => `module.exports = (ts, getNameFromJsxTag) => {\n${ lines . join ( '\n' ) } \nreturn ${ returnModuleCode } }`
87
+ let moduleString = getModuleString ( )
88
+ if ( isTs5 ) {
89
+ const { languageService } = createLanguageService ( {
90
+ 'main.ts' : moduleString ,
91
+ } )
92
+ const notFoundVariables = new Set < string > ( )
93
+ for ( const { messageText } of languageService . getSemanticDiagnostics ( 'main.ts' ) ) {
94
+ const notFoundName = ( typeof messageText === 'object' ? messageText . messageText : messageText ) . match ( / ^ C a n n o t f i n d n a m e ' ( .+ ?) ' ./ ) ?. [ 1 ]
95
+ if ( ! notFoundName ) continue
96
+ notFoundVariables . add ( notFoundName )
45
97
}
98
+ lines . unshift ( `const {${ [ ...notFoundVariables . keys ( ) ] . join ( ', ' ) } } = ts;` )
99
+ moduleString = getModuleString ( )
46
100
}
47
- const getModule = nodeModules ! . requireFromString ( 'module.exports = (ts, getNameFromJsxTag) => {' + lines . join ( '\n' ) + 'return NavigationBar;}' )
101
+ const getModule = nodeModules ! . requireFromString ( moduleString )
48
102
const getNameFromJsxTag = ( node : ts . JsxSelfClosingElement | ts . JsxOpeningElement ) => {
49
103
const {
50
104
attributes : { properties } ,
@@ -72,10 +126,10 @@ const getPatchedNavModule = () => {
72
126
return getModule ( ts , getNameFromJsxTag )
73
127
}
74
128
75
- let navModule
129
+ let navModule : { getNavigationTree : any }
76
130
77
131
export const getNavTreeItems = ( info : ts . server . PluginCreateInfo , fileName : string ) => {
78
- if ( ! navModule ) navModule = getPatchedNavModule ( )
132
+ /* if (!navModule) */ navModule = getPatchedNavModule ( )
79
133
const program = info . languageService . getProgram ( )
80
134
if ( ! program ) throw new Error ( 'no program' )
81
135
const sourceFile = program ?. getSourceFile ( fileName )
0 commit comments