11import { nodeModules } from './utils'
2+ import * as semver from 'semver'
3+ import { createLanguageService } from './dummyLanguageService'
24
3- // uses at testing only
5+ // used at testing only
46declare const __TS_SEVER_PATH__ : string | undefined
57
6- const getPatchedNavModule = ( ) => {
8+ const getPatchedNavModule = ( ) : { getNavigationTree ( ...args ) } => {
9+ // what is happening here: grabbing & patching NavigationBar module contents from actual running JS
710 const tsServerPath = typeof __TS_SEVER_PATH__ !== 'undefined' ? __TS_SEVER_PATH__ : require . main ! . filename
11+ // current lib/tsserver.js
812 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
1422 linesOffset : number
1523 addString ?: string
1624 removeLines ?: number
17- } [ ] = [
25+ }
26+
27+ const patchLocations : PatchLocation [ ] = [
1828 {
19- predicateString : 'function addChildrenRecursively(node)' ,
29+ searchString : 'function addChildrenRecursively(node)' ,
2030 linesOffset : 7 ,
2131 addString : `
2232 case ts.SyntaxKind.JsxSelfClosingElement:
@@ -29,7 +39,7 @@ const getPatchedNavModule = () => {
2939 break` ,
3040 } ,
3141 {
32- predicateString : 'return "<unknown>";' ,
42+ searchString : 'return "<unknown>";' ,
3343 linesOffset : - 1 ,
3444 addString : `
3545 case ts.SyntaxKind.JsxSelfClosingElement:
@@ -38,13 +48,57 @@ const getPatchedNavModule = () => {
3848 return getNameFromJsxTag(node.openingElement);` ,
3949 } ,
4050 ]
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 ) )
4380 if ( addTypeIndex !== - 1 ) {
4481 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 )
4597 }
98+ lines . unshift ( `const {${ [ ...notFoundVariables . keys ( ) ] . join ( ', ' ) } } = ts;` )
99+ moduleString = getModuleString ( )
46100 }
47- const getModule = nodeModules ! . requireFromString ( 'module.exports = (ts, getNameFromJsxTag) => {' + lines . join ( '\n' ) + 'return NavigationBar;}' )
101+ const getModule = nodeModules ! . requireFromString ( moduleString )
48102 const getNameFromJsxTag = ( node : ts . JsxSelfClosingElement | ts . JsxOpeningElement ) => {
49103 const {
50104 attributes : { properties } ,
@@ -72,10 +126,10 @@ const getPatchedNavModule = () => {
72126 return getModule ( ts , getNameFromJsxTag )
73127}
74128
75- let navModule
129+ let navModule : { getNavigationTree : any }
76130
77131export const getNavTreeItems = ( info : ts . server . PluginCreateInfo , fileName : string ) => {
78- if ( ! navModule ) navModule = getPatchedNavModule ( )
132+ /* if (!navModule) */ navModule = getPatchedNavModule ( )
79133 const program = info . languageService . getProgram ( )
80134 if ( ! program ) throw new Error ( 'no program' )
81135 const sourceFile = program ?. getSourceFile ( fileName )
0 commit comments