1- import fs , { readFileSync , readdirSync , statSync } from 'fs' ;
1+ import fs , { readFileSync } from 'fs' ;
22import * as tern from '../tern/lib/tern' ;
33import path from 'path' ;
44import * as util from 'util' ;
55import * as infer from '../tern/lib/infer' ;
66import LineColumnFinder from 'line-column' ;
77import { findPreviousWord , findPreviousLeftParan , countPreviousCommas } from './string-util' ;
88import URI from 'vscode-uri' ;
9+ import browser from '../tern/defs/browser.json' ;
10+ import ecmascript from '../tern/defs/ecmascript.json' ;
911
1012import { memoize } from '@salesforce/lightning-lsp-common/lib/utils' ;
1113import {
@@ -43,7 +45,7 @@ interface TernServer extends tern.Server {
4345 * The `callback` function will be called when the request completes. If an `error` occurred,
4446 * it will be passed as a first argument. Otherwise, the `response` (parsed) JSON object will be passed as second argument.
4547 *
46- * When the server hasn’ t been configured to be asynchronous, the callback will be called before request returns.
48+ * When the server hasn' t been configured to be asynchronous, the callback will be called before request returns.
4749 */
4850 request ( doc : any , callback : any ) : void ;
4951}
@@ -57,9 +59,6 @@ let ternServer: TernServer;
5759let asyncTernRequest ;
5860let asyncFlush ;
5961
60- const defaultLibs = [ 'browser' , 'ecmascript' ] ;
61- const defaultPlugins = { modules : { } , aura : { } , doc_comment : { } } ;
62-
6362const defaultConfig = {
6463 ecmaVersion : 6 ,
6564 stripCRs : false ,
@@ -70,107 +69,33 @@ const defaultConfig = {
7069 dependencyBudget : 20000 ,
7170} ;
7271
73- function readJSON ( fileName ) : any {
74- const file = fs . readFileSync ( fileName , 'utf-8' ) ;
75- try {
76- return JSON . parse ( file ) ;
77- } catch ( e ) {
78- console . warn ( 'Bad JSON in ' + fileName + ': ' + e . message ) ;
79- }
80- }
81-
82- function findDefs ( libs ) : any [ ] {
83- const ternlibpath = require . resolve ( '../tern/lib/tern' ) ;
84- const ternbasedir = path . join ( ternlibpath , '..' , '..' ) ;
85-
86- const defs = [ ] ;
87- const src = libs . slice ( ) ;
88- for ( let file of src ) {
89- console . log ( `Loading support library: ${ file } ` ) ;
90- if ( ! / \. j s o n $ / . test ( file ) ) {
91- file = file + '.json' ;
92- }
93- const def = path . join ( ternbasedir , 'defs' , file ) ;
94- if ( fs . existsSync ( def ) ) {
95- defs . push ( readJSON ( def ) ) ;
96- } else {
97- console . log ( `Not found: ${ file } ` ) ;
98- }
99- }
100- return defs ;
101- }
102-
103- async function loadLocal ( plugin , rootPath ) : Promise < boolean > {
104- let found ;
105- try {
106- // local resolution only here
107- found = require . resolve ( './tern-' + plugin ) ;
108- } catch ( e ) {
109- return false ;
110- }
111-
112- const mod = await import ( found ) ;
113- if ( mod . hasOwnProperty ( 'initialize' ) ) {
114- mod . initialize ( rootPath ) ;
115- }
116- return true ;
117- }
72+ const auraInstanceLastSort = ( a : string , b : string ) : number =>
73+ a . endsWith ( 'AuraInstance.js' ) === b . endsWith ( 'AuraInstance.js' ) ? 0 : a . endsWith ( 'AuraInstance.js' ) ? 1 : - 1 ;
11874
119- async function loadBuiltIn ( plugin : string , rootPath : string ) : Promise < boolean > {
120- const ternlibpath = require . resolve ( '../tern/lib/tern' ) ;
121- const ternbasedir = path . join ( ternlibpath , '..' , '..' ) ;
75+ async function loadPlugins ( ) : Promise < { aura : true ; modules : true ; doc_comment : true } > {
76+ await import ( './tern-aura' ) ;
77+ await import ( '../tern/plugin/modules' ) ;
78+ await import ( '../tern/plugin/doc_comment' ) ;
12279
123- const def = path . join ( ternbasedir , 'plugin' , plugin ) ;
124-
125- let found : string ;
126- try {
127- // local resolution only here
128- found = require . resolve ( def ) ;
129- } catch ( e ) {
130- process . stderr . write ( 'Failed to find plugin ' + plugin + '.\n' ) ;
131- return false ;
132- }
133-
134- const mod = await import ( found ) ;
135- if ( mod . hasOwnProperty ( 'initialize' ) ) {
136- mod . initialize ( rootPath ) ;
137- }
138- return true ;
80+ return {
81+ aura : true ,
82+ modules : true ,
83+ doc_comment : true ,
84+ } ;
13985}
14086
141- async function loadPlugins ( plugins , rootPath ) : Promise < { } > {
142- const options = { } ;
143- for ( const plugin of Object . keys ( plugins ) ) {
144- const val = plugins [ plugin ] ;
145- if ( ! val ) {
146- continue ;
147- }
148-
149- if ( ! ( await loadLocal ( plugin , rootPath ) ) ) {
150- if ( ! ( await loadBuiltIn ( plugin , rootPath ) ) ) {
151- process . stderr . write ( 'Failed to find plugin ' + plugin + '.\n' ) ;
152- }
153- }
154-
155- options [ path . basename ( plugin ) ] = true ;
87+ /** recursively search upward from the starting diretory. Handling the is it a monorepo vs. packaged vs. bundled code */
88+ const searchAuraResourcesPath = ( dir : string ) : string => {
89+ console . log ( `aura-language-server: searching for resources/aura in ${ dir } ` ) ;
90+ if ( fs . existsSync ( path . join ( dir , 'resources' , 'aura' ) ) ) {
91+ console . log ( 'found resources/aura in' , dir ) ;
92+ return path . join ( dir , 'resources' , 'aura' ) ;
15693 }
157-
158- return options ;
159- }
160-
161- function * walkSync ( dir : string ) : any {
162- const files = readdirSync ( dir ) ;
163-
164- for ( const file of files ) {
165- const pathToFile = path . join ( dir , file ) ;
166- const isDirectory = statSync ( pathToFile ) . isDirectory ( ) ;
167- if ( isDirectory ) {
168- yield * walkSync ( pathToFile ) ;
169- } else {
170- yield pathToFile ;
171- }
94+ if ( path . dirname ( dir ) === dir ) {
95+ throw new Error ( 'No resources/aura directory found' ) ;
17296 }
173- }
97+ return searchAuraResourcesPath ( path . dirname ( dir ) ) ;
98+ } ;
17499
175100async function ternInit ( ) : Promise < void > {
176101 await asyncTernRequest ( {
@@ -180,29 +105,27 @@ async function ternInit(): Promise<void> {
180105 // shouldFilter: true,
181106 } ,
182107 } ) ;
183- const resources = path . join ( __dirname , '..' , '..' , 'resources' , 'aura' ) ;
184- const found = [ ...walkSync ( resources ) ] ;
185- let [ lastFile , lastText ] = [ undefined , undefined ] ;
186- for ( const file of found ) {
187- if ( file . endsWith ( '.js' ) ) {
188- const data = readFileSync ( file , 'utf-8' ) ;
189- // HACK HACK HACK - glue it all together baby!
190- if ( file . endsWith ( 'AuraInstance.js' ) ) {
191- lastFile = file ;
192- lastText = data . concat ( `\nwindow['$A'] = new AuraInstance();\n` ) ;
193- } else {
194- ternServer . addFile ( file , data ) ;
195- }
196- }
197- }
198- ternServer . addFile ( lastFile , lastText ) ;
108+ const resources = searchAuraResourcesPath ( __dirname ) ;
109+ ( await fs . promises . readdir ( resources , { withFileTypes : true , recursive : true } ) )
110+ . filter ( dirent => dirent . isFile ( ) && dirent . name . endsWith ( '.js' ) )
111+ . map ( dirent => path . join ( dirent . parentPath , dirent . name ) )
112+ // special handling for hacking one snowflake file that needs to go last
113+ . sort ( auraInstanceLastSort )
114+ . map ( file => ( {
115+ file,
116+ contents : file . endsWith ( 'AuraInstance.js' )
117+ ? // and the snowflake needs to me modified
118+ readFileSync ( file , 'utf-8' ) . concat ( `\nwindow['$A'] = new AuraInstance();\n` )
119+ : readFileSync ( file , 'utf-8' ) ,
120+ } ) )
121+ . map ( ( { file, contents } ) => ternServer . addFile ( file , contents ) ) ;
199122}
200123
201124const init = memoize ( ternInit ) ;
202125
203126export async function startServer ( rootPath : string , wsroot : string ) : tern . Server {
204- const defs = findDefs ( defaultLibs ) ;
205- const plugins = await loadPlugins ( defaultPlugins , rootPath ) ;
127+ const defs = [ browser , ecmascript ] ;
128+ const plugins = await loadPlugins ( ) ;
206129 const config : tern . ConstructorOptions = {
207130 ...defaultConfig ,
208131 defs,
@@ -239,6 +162,12 @@ function fileToUri(file: string): string {
239162 }
240163}
241164
165+ function uriToFile ( uri : string ) : string {
166+ const parsedUri = URI . parse ( uri ) ;
167+ // paths from tests can be relative or absolute
168+ return parsedUri . scheme ? parsedUri . fsPath : uri ;
169+ }
170+
242171function tern2lspRange ( { start, end } : { start : tern . Position ; end : tern . Position } ) : Range {
243172 return {
244173 start : tern2lspPos ( start ) ,
@@ -253,10 +182,6 @@ function tern2lspLocation({ file, start, end }: { file: string; start: tern.Posi
253182 } ;
254183}
255184
256- function uriToFile ( uri : string ) : string {
257- return URI . parse ( uri ) . fsPath ;
258- }
259-
260185async function ternRequest ( event : TextDocumentPositionParams , type : string , options : any = { } ) : Promise < any > {
261186 return await asyncTernRequest ( {
262187 query : {
0 commit comments