Skip to content

Commit 14cc0cc

Browse files
committed
almost here, need to do actual text changes in place
1 parent afe38d9 commit 14cc0cc

File tree

1 file changed

+69
-15
lines changed

1 file changed

+69
-15
lines changed

typescript/src/getPatchedNavTree.ts

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,32 @@
11
import { 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
46
declare 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(/^Cannot find name '(.+?)'./)?.[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

77131
export 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

Comments
 (0)