Name AMD module `foo` rather than `foo/module_root/index` We already had logic to name it foo rather than foo/index, introduced in https://github.com/bazelbuild/rules_typescript/commit/c7b6880767eaae44d8777a7a1b137de92b8fd4d9 If the module_root is specified as a directory on the ts_library, it means that the index file is in that subdirectory of the package. We already handled this case correctly in type-checking, but at runtime the module identifiers don't match so the require('foo/module_root/index') fails. Fixes bazelbuild/rules_nodejs#973 PiperOrigin-RevId: 262663195
diff --git a/internal/tsc_wrapped/compiler_host.ts b/internal/tsc_wrapped/compiler_host.ts index 9008a08..9cae018 100644 --- a/internal/tsc_wrapped/compiler_host.ts +++ b/internal/tsc_wrapped/compiler_host.ts
@@ -333,11 +333,14 @@ if (this.bazelOpts.moduleName) { const relativeFileName = path.posix.relative(this.bazelOpts.package, fileName); + // check that the fileName was actually underneath the package directory if (!relativeFileName.startsWith('..')) { - if (this.bazelOpts.moduleRoot && - this.bazelOpts.moduleRoot.replace(SOURCE_EXT, '') === - relativeFileName) { - return this.bazelOpts.moduleName; + if (this.bazelOpts.moduleRoot) { + const root = this.bazelOpts.moduleRoot.replace(SOURCE_EXT, ''); + if (root === relativeFileName || + path.posix.join(root, 'index') === relativeFileName) { + return this.bazelOpts.moduleName; + } } // Support the common case of commonjs convention that index is the // default module in a directory.
diff --git a/internal/tsc_wrapped/compiler_host_test.ts b/internal/tsc_wrapped/compiler_host_test.ts new file mode 100644 index 0000000..eaa0ac4 --- /dev/null +++ b/internal/tsc_wrapped/compiler_host_test.ts
@@ -0,0 +1,62 @@ +import 'jasmine'; + +import * as ts from 'typescript'; + +import {CompilerHost} from './compiler_host'; +import {BazelOptions} from './tsconfig'; + +describe('compiler host', () => { + describe('computes the amd module name of a .ts source file', () => { + const options: ts.CompilerOptions = { + rootDirs: [], + rootDir: 'base', + outDir: 'out', + }; + const bazelOptions: BazelOptions = { + package: 'path/to/package', + compilationTargetSrc: [ + 'path/to/package/index.ts', + 'path/to/package/root_dir/index.ts', + 'test.ts', + ], + workspaceName: 'my_wksp', + } as any; + + const defaultHost = + new CompilerHost([], options, bazelOptions, null as any, null as any); + // A module is a file with at least an import or export statement. + function createTsModule(filename: string) { + return ts.createSourceFile(filename, 'export {}', ts.ScriptTarget.ES2015); + } + + it('should name a module after the workspace and filename', () => { + expect(defaultHost.amdModuleName(createTsModule('test.ts'))) + .toBe('my_wksp/test'); + }); + + it('should not provide a name for files that are not in the compilation unit', + () => { + expect( + defaultHost.amdModuleName(createTsModule('some_other_file.d.ts'))) + .toBeUndefined(); + }); + + it('should name the index file with a short name', () => { + const host = new CompilerHost( + [], options, {...bazelOptions, moduleName: 'my_lib'}, null as any, + null as any); + expect(host.amdModuleName(createTsModule('path/to/package/index.ts'))) + .toBe('my_lib'); + }); + it('should name an index file under a module_root with a short name', + () => { + const host = new CompilerHost( + [], options, + {...bazelOptions, moduleName: 'my_lib', moduleRoot: 'root_dir'}, + null as any, null as any); + expect(host.amdModuleName( + createTsModule('path/to/package/root_dir/index.ts'))) + .toBe('my_lib'); + }); + }); +});