11/* eslint-disable no-console */
2+ import * as ts from 'typescript' ;
23import { promises as fs } from 'fs' ;
34import path from 'path' ;
45import { replaceImports } from '../src/utils' ;
56
67async function loadSources ( sources : Record < string , string > ) {
7- const result : Record < string , string > = { } ;
8+ const result : Record < string , string | boolean > = { } ;
89
9- for ( const [ key , filepath ] of Object . entries ( sources ) ) {
10- result [ key ] = replaceImports ( await fs . readFile ( filepath , 'utf8' ) ) ;
10+ for ( const [ key , filePath ] of Object . entries ( sources ) ) {
11+ // for .js filepaths we're never going to read them, so just make the
12+ // value true as an optimisation so we can still know that they should
13+ // exist during the language server's module resolution.
14+ try {
15+ const data = filePath . endsWith ( '.js' )
16+ ? true
17+ : replaceImports ( await fs . readFile ( filePath , 'utf8' ) ) ;
18+ result [ key ] = data ;
19+ } catch ( err : any ) {
20+ if ( err . code !== 'ENOENT' ) {
21+ console . error ( `Error reading file ${ filePath } :` , err ) ;
22+ throw err ;
23+ }
24+ }
1125 }
1226
1327 return result ;
1428}
1529
30+ function resolve ( moduleName : string ) {
31+ if ( moduleName === '@mongodb-js/mongodb-ts-autocomplete' ) {
32+ // There's a chicken-and-egg problem here: we can't compile this module
33+ // itself without the extracted types and we can't extract it before it is
34+ // compiled because the module is not compiled, so its dist/index.js does
35+ // not exist yet. With this workaround we're resolving the package.json
36+ // which definitely already exists.
37+ const result = {
38+ resolvedModule : {
39+ resolvedFileName : path . resolve ( __dirname , '..' , 'package.json' ) ,
40+ packageId : {
41+ subModuleName : 'package.json' ,
42+ } ,
43+ } ,
44+ } ;
45+
46+ return result ;
47+ }
48+ const result = ts . resolveModuleName (
49+ moduleName ,
50+ __filename ,
51+ { } ,
52+ {
53+ fileExists : ( path : string ) => ts . sys . fileExists ( path ) ,
54+ readFile : ( path : string ) => ts . sys . readFile ( path ) ,
55+ } ,
56+ ) ;
57+
58+ return result ;
59+ }
60+
61+ function resolveModulePath ( moduleName : string ) : string {
62+ const { resolvedModule } = resolve ( moduleName ) ;
63+ if ( ! resolvedModule ) {
64+ throw new Error ( `Could not resolve module: ${ moduleName } ` ) ;
65+ }
66+ return resolvedModule . resolvedFileName ;
67+ }
68+
69+ const deps : Record < string , string [ ] > = {
70+ '@mongodb-js/mongodb-ts-autocomplete' : [
71+ // the module resolution won't be addressing this module by name, but we'll
72+ // feed it this package.json as a fallback when it tries to find itself
73+ 'package.json' ,
74+ ] ,
75+ '@types/node' : [
76+ 'package.json' ,
77+ 'assert.d.ts' ,
78+ 'assert/strict.d.ts' ,
79+ 'async_hooks.d.ts' ,
80+ 'buffer.buffer.d.ts' ,
81+ 'buffer.d.ts' ,
82+ 'child_process.d.ts' ,
83+ 'cluster.d.ts' ,
84+ 'compatibility/disposable.d.ts' ,
85+ 'compatibility/index.d.ts' ,
86+ 'compatibility/indexable.d.ts' ,
87+ 'compatibility/iterators.d.ts' ,
88+ 'console.d.ts' ,
89+ 'constants.d.ts' ,
90+ 'crypto.d.ts' ,
91+ 'dgram.d.ts' ,
92+ 'diagnostics_channel.d.ts' ,
93+ 'dns.d.ts' ,
94+ 'dns/promises.d.ts' ,
95+ 'dom-events.d.ts' ,
96+ 'domain.d.ts' ,
97+ 'events.d.ts' ,
98+ 'fs.d.ts' ,
99+ 'fs/promises.d.ts' ,
100+ 'globals.d.ts' ,
101+ 'globals.typedarray.d.ts' ,
102+ 'http.d.ts' ,
103+ 'http2.d.ts' ,
104+ 'https.d.ts' ,
105+ 'index.d.ts' ,
106+ 'inspector.d.ts' ,
107+ 'module.d.ts' ,
108+ 'net.d.ts' ,
109+ 'os.d.ts' ,
110+ 'path.d.ts' ,
111+ 'perf_hooks.d.ts' ,
112+ 'process.d.ts' ,
113+ 'punycode.d.ts' ,
114+ 'querystring.d.ts' ,
115+ 'readline.d.ts' ,
116+ 'readline/promises.d.ts' ,
117+ 'repl.d.ts' ,
118+ 'sea.d.ts' ,
119+ 'sqlite.d.ts' ,
120+ 'stream.d.ts' ,
121+ 'stream/consumers.d.ts' ,
122+ 'stream/promises.d.ts' ,
123+ 'stream/web.d.ts' ,
124+ 'string_decoder.d.ts' ,
125+ 'test.d.ts' ,
126+ 'timers.d.ts' ,
127+ 'timers/promises.d.ts' ,
128+ 'tls.d.ts' ,
129+ 'trace_events.d.ts' ,
130+ 'tty.d.ts' ,
131+ 'url.d.ts' ,
132+ 'util.d.ts' ,
133+ 'v8.d.ts' ,
134+ 'vm.d.ts' ,
135+ 'wasi.d.ts' ,
136+ 'worker_threads.d.ts' ,
137+ 'zlib.d.ts' ,
138+ ] ,
139+ assert : [
140+ 'package.json' ,
141+ 'assert.js' , // exists only
142+ ] ,
143+ buffer : [ 'package.json' , 'index.d.ts' ] ,
144+ events : [ 'package.json' ] ,
145+ punycode : [
146+ 'package.json' ,
147+ 'punycode.js' , // exists only
148+ ] ,
149+ querystring : [
150+ 'package.json' ,
151+ 'index.js' , // exists only
152+ ] ,
153+ string_decoder : [
154+ 'package.json' ,
155+ 'lib/string_decoder.js' , // exists only
156+ ] ,
157+ typescript : [
158+ 'package.json' ,
159+ 'lib/es2023.ts' ,
160+ 'lib/lib.decorators.d.ts' ,
161+ 'lib/lib.decorators.legacy.d.ts' ,
162+ 'lib/lib.es2015.collection.d.ts' ,
163+ 'lib/lib.es2015.core.d.ts' ,
164+ 'lib/lib.es2015.d.ts' ,
165+ 'lib/lib.es2015.generator.d.ts' ,
166+ 'lib/lib.es2015.iterable.d.ts' ,
167+ 'lib/lib.es2015.promise.d.ts' ,
168+ 'lib/lib.es2015.proxy.d.ts' ,
169+ 'lib/lib.es2015.reflect.d.ts' ,
170+ 'lib/lib.es2015.symbol.d.ts' ,
171+ 'lib/lib.es2015.symbol.wellknown.d.ts' ,
172+ 'lib/lib.es2016.array.include.d.ts' ,
173+ 'lib/lib.es2016.d.ts' ,
174+ 'lib/lib.es2016.intl.d.ts' ,
175+ 'lib/lib.es2017.arraybuffer.d.ts' ,
176+ 'lib/lib.es2017.d.ts' ,
177+ 'lib/lib.es2017.date.d.ts' ,
178+ 'lib/lib.es2017.intl.d.ts' ,
179+ 'lib/lib.es2017.object.d.ts' ,
180+ 'lib/lib.es2017.sharedmemory.d.ts' ,
181+ 'lib/lib.es2017.string.d.ts' ,
182+ 'lib/lib.es2017.typedarrays.d.ts' ,
183+ 'lib/lib.es2018.asyncgenerator.d.ts' ,
184+ 'lib/lib.es2018.asynciterable.d.ts' ,
185+ 'lib/lib.es2018.d.ts' ,
186+ 'lib/lib.es2018.intl.d.ts' ,
187+ 'lib/lib.es2018.promise.d.ts' ,
188+ 'lib/lib.es2018.regexp.d.ts' ,
189+ 'lib/lib.es2019.array.d.ts' ,
190+ 'lib/lib.es2019.d.ts' ,
191+ 'lib/lib.es2019.intl.d.ts' ,
192+ 'lib/lib.es2019.object.d.ts' ,
193+ 'lib/lib.es2019.string.d.ts' ,
194+ 'lib/lib.es2019.symbol.d.ts' ,
195+ 'lib/lib.es2020.bigint.d.ts' ,
196+ 'lib/lib.es2020.d.ts' ,
197+ 'lib/lib.es2020.date.d.ts' ,
198+ 'lib/lib.es2020.intl.d.ts' ,
199+ 'lib/lib.es2020.number.d.ts' ,
200+ 'lib/lib.es2020.promise.d.ts' ,
201+ 'lib/lib.es2020.sharedmemory.d.ts' ,
202+ 'lib/lib.es2020.string.d.ts' ,
203+ 'lib/lib.es2020.symbol.wellknown.d.ts' ,
204+ 'lib/lib.es5.d.ts' ,
205+ ] ,
206+ 'undici-types' : [ 'package.json' , 'index.d.ts' ] ,
207+ url : [
208+ 'package.json' ,
209+ 'url.js' , // exists only
210+ ] ,
211+ util : [
212+ 'package.json' ,
213+ 'util.js' , // exists only
214+ ] ,
215+ } ;
216+
16217async function run ( ) {
218+ const mqlPath = path . join (
219+ path . dirname ( resolveModulePath ( '@mongodb-js/mql-typescript' ) ) ,
220+ '..' ,
221+ 'out' ,
222+ 'schema.d.ts' ,
223+ ) ;
224+
17225 const input : Record < string , string > = {
18226 // mql imports bson but right now so does shell-api. We could bake the types
19227 // those use into the files we generate using api-extractor, but maybe
20228 // including it just once is not so bad.
21- '/bson.ts' : path . join ( require . resolve ( 'bson' ) , '..' , '..' , 'bson.d.ts ') ,
229+ '/bson.ts' : resolveModulePath ( 'bson' ) ,
22230 // mql imports the mongodb driver. We could also use api-extractor there to
23231 // bake the few mongodb types we use into the schema.
24- '/mongodb.ts' : path . join (
25- require . resolve ( 'mongodb' ) ,
26- '..' ,
27- '..' ,
28- 'mongodb.d.ts' ,
29- ) ,
232+ '/mongodb.ts' : resolveModulePath ( 'mongodb' ) ,
30233 // We wouldn't have to include mql if we used it straight from shell-api,
31234 // but since we're using it straight here for now to bypass the complicated
32235 // generics on the shell-api side it is included here for now.
33- '/mql.ts' : path . join (
34- require . resolve ( '@mongodb-js/mql-typescript' ) ,
35- '..' ,
36- '..' ,
37- 'out' ,
38- 'schema.d.ts' ,
39- ) ,
236+ '/mql.ts' : mqlPath ,
40237 } ;
238+ for ( const [ moduleName , filePaths ] of Object . entries ( deps ) ) {
239+ const { resolvedModule } = resolve ( moduleName ) ;
240+ if ( ! resolvedModule || ! resolvedModule . packageId ) {
241+ throw new Error ( `Could not resolve module: ${ moduleName } ` ) ;
242+ }
243+
244+ const basePath = resolvedModule . resolvedFileName . slice (
245+ 0 ,
246+ - resolvedModule . packageId . subModuleName . length ,
247+ ) ;
248+ for ( const filePath of filePaths ) {
249+ const fullPath = path . join ( basePath , filePath ) ;
250+ // these are in the format import of typescript imports
251+ input [ `${ moduleName } /${ filePath } ` ] = fullPath ;
252+ }
253+ }
254+
41255 const files = await loadSources ( input ) ;
42256 const code = `
43257const files = ${ JSON . stringify ( files ) } ;
@@ -48,7 +262,7 @@ export default files;
48262 __dirname ,
49263 '..' ,
50264 'src' ,
51- 'fixtures ' ,
265+ 'types ' ,
52266 'autocomplete-types.ts' ,
53267 ) ;
54268 console . log ( filepath ) ;
0 commit comments