@@ -190,8 +190,12 @@ function runFromOptions(
190
190
const compilerHostDelegate =
191
191
ts . createCompilerHost ( { target : ts . ScriptTarget . ES5 } ) ;
192
192
193
+ const moduleResolver = bazelOpts . isJsTranspilation ?
194
+ makeJsModuleResolver ( bazelOpts . workspaceName ) :
195
+ ts . resolveModuleName ;
193
196
const compilerHost = new CompilerHost (
194
- files , options , bazelOpts , compilerHostDelegate , fileLoader ) ;
197
+ files , options , bazelOpts , compilerHostDelegate , fileLoader ,
198
+ moduleResolver ) ;
195
199
196
200
197
201
const oldProgram = cache . getProgram ( bazelOpts . target ) ;
@@ -332,6 +336,70 @@ function mkdirp(base: string, subdir: string) {
332
336
}
333
337
334
338
339
+ /**
340
+ * Resolve module filenames for JS modules.
341
+ *
342
+ * JS module resolution needs to be different because when transpiling JS we
343
+ * do not pass in any dependencies, so the TS module resolver will not resolve
344
+ * any files.
345
+ *
346
+ * Fortunately, JS module resolution is very simple. The imported module name
347
+ * must either a relative path, or the workspace root (i.e. 'google3'),
348
+ * so we can perform module resolution entirely based on file names, without
349
+ * looking at the filesystem.
350
+ */
351
+ function makeJsModuleResolver ( workspaceName : string ) {
352
+ // The literal '/' here is cross-platform safe because it's matching on
353
+ // import specifiers, not file names.
354
+ const workspaceModuleSpecifierPrefix = `${ workspaceName } /` ;
355
+ const workspaceDir = `${ path . sep } ${ workspaceName } ${ path . sep } ` ;
356
+ function jsModuleResolver (
357
+ moduleName : string , containingFile : string ,
358
+ compilerOptions : ts . CompilerOptions , host : ts . ModuleResolutionHost ) :
359
+ ts . ResolvedModuleWithFailedLookupLocations {
360
+ let resolvedFileName ;
361
+ if ( containingFile === '' ) {
362
+ // In tsickle we resolve the filename against '' to get the goog module
363
+ // name of a sourcefile.
364
+ resolvedFileName = moduleName ;
365
+ } else if ( moduleName . startsWith ( workspaceModuleSpecifierPrefix ) ) {
366
+ // Given a workspace name of 'foo', we want to resolve import specifiers
367
+ // like: 'foo/project/file.js' to the absolute filesystem path of
368
+ // project/file.js within the workspace.
369
+ const workspaceDirLocation = containingFile . indexOf ( workspaceDir ) ;
370
+ if ( workspaceDirLocation < 0 ) {
371
+ return { resolvedModule : undefined } ;
372
+ }
373
+ const absolutePathToWorkspaceDir =
374
+ containingFile . slice ( 0 , workspaceDirLocation ) ;
375
+ resolvedFileName = path . join ( absolutePathToWorkspaceDir , moduleName ) ;
376
+ } else {
377
+ if ( ! moduleName . startsWith ( './' ) && ! moduleName . startsWith ( '../' ) ) {
378
+ throw new Error (
379
+ `Unsupported module import specifier: ${
380
+ JSON . stringify ( moduleName ) } .\n` +
381
+ `JS module imports must either be relative paths ` +
382
+ `(beginning with '.' or '..'), ` +
383
+ `or they must begin with '${ workspaceName } /'.` ) ;
384
+ }
385
+ resolvedFileName = path . join ( path . dirname ( containingFile ) , moduleName ) ;
386
+ }
387
+ return {
388
+ resolvedModule : {
389
+ resolvedFileName,
390
+ extension : ts . Extension . Js , // js can only import js
391
+ // These two fields are cargo culted from what ts.resolveModuleName
392
+ // seems to return.
393
+ packageId : undefined ,
394
+ isExternalLibraryImport : false ,
395
+ }
396
+ } ;
397
+ }
398
+
399
+ return jsModuleResolver ;
400
+ }
401
+
402
+
335
403
if ( require . main === module ) {
336
404
// Do not call process.exit(), as that terminates the binary before
337
405
// completing pending operations, such as writing to stdout or emitting the
0 commit comments