- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 434
          Set webpack module type to "javascript/esm" when TS impliedNodeFormat is ESNext
          #1614
        
          New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d490c50
              c3fc615
              4712067
              394c7a7
              3ea6e50
              a2cbf15
              6d31cf6
              379e771
              2888b33
              e436dd0
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -26,6 +26,7 @@ import { | |
| appendSuffixesIfMatch, | ||
| ensureProgram, | ||
| formatErrors, | ||
| getImpliedNodeFormat, | ||
| isReferencedFile, | ||
| makeError, | ||
| supportsSolutionBuild, | ||
|  | @@ -363,7 +364,7 @@ export function initializeInstance( | |
| : instance.compiler.createProgram([], instance.compilerOptions)); | ||
|  | ||
| const getProgram = () => program; | ||
| instance.transformers = getCustomTransformers(instance.loaderOptions, program, getProgram); | ||
| instance.transformers = getCustomTransformers(instance, loader, program, getProgram); | ||
| // Setup watch run for solution building | ||
| if (instance.solutionBuilderHost) { | ||
| addAssetHooks(loader, instance); | ||
|  | @@ -394,11 +395,12 @@ export function initializeInstance( | |
| instance.builderProgram = | ||
| instance.watchOfFilesAndCompilerOptions.getProgram(); | ||
|  | ||
| const getProgram = () => instance.builderProgram?.getProgram(); | ||
| const getProgram = () => instance.builderProgram!.getProgram(); | ||
| instance.program = getProgram(); | ||
| instance.transformers = getCustomTransformers( | ||
| instance.loaderOptions, | ||
| instance.program, | ||
| instance, | ||
| loader, | ||
| instance.program!, | ||
| getProgram | ||
| ); | ||
| } else { | ||
|  | @@ -414,8 +416,8 @@ export function initializeInstance( | |
| instance.compiler.createDocumentRegistry() | ||
| ); | ||
|  | ||
| const getProgram = () => instance.languageService!.getProgram(); | ||
| instance.transformers = getCustomTransformers(instance.loaderOptions, getProgram(), getProgram); | ||
| const getProgram = () => instance.languageService!.getProgram()!; | ||
| instance.transformers = getCustomTransformers(instance, loader, getProgram(), getProgram); | ||
| } | ||
|  | ||
| addAssetHooks(loader, instance); | ||
|  | @@ -427,14 +429,25 @@ export function initializeInstance( | |
| } | ||
| } | ||
|  | ||
| function getSetImpliedNodeFormatTransformer(instance: TSInstance, loaderContext: webpack.LoaderContext<LoaderOptions>, getProgram: () => typescript.Program) { | ||
| return (): typescript.Transformer<typescript.SourceFile> => { | ||
| return (sourceFile) => { | ||
| sourceFile.impliedNodeFormat = getImpliedNodeFormat(sourceFile.fileName, instance, loaderContext, getProgram()); | ||
| return sourceFile; | ||
| } | ||
| } | ||
| } | ||
|  | ||
| export function getCustomTransformers( | ||
| loaderOptions: LoaderOptions, | ||
| program: typescript.Program | undefined, | ||
| getProgram: (() => typescript.Program | undefined) | undefined | ||
| instance: TSInstance, | ||
| loaderContext: webpack.LoaderContext<LoaderOptions>, | ||
| program: typescript.Program, | ||
| getProgram: (() => typescript.Program) | ||
| ) { | ||
| // same strategy as https://github.com/s-panferov/awesome-typescript-loader/pull/531/files | ||
| const { loaderOptions } = instance; | ||
| let { getCustomTransformers: customerTransformers } = loaderOptions; | ||
| let getCustomTransformers = Function.prototype; | ||
| let getCustomTransformers; | ||
|  | ||
| if (typeof customerTransformers === 'function') { | ||
| getCustomTransformers = customerTransformers; | ||
|  | @@ -459,7 +472,14 @@ export function getCustomTransformers( | |
| getCustomTransformers = customerTransformers; | ||
| } | ||
|  | ||
| return getCustomTransformers(program, getProgram); | ||
| let transformers = getCustomTransformers?.(program, getProgram); | ||
| if (loaderOptions.transpileOnly) { | ||
| (transformers ??= {}).before = [ | ||
| getSetImpliedNodeFormatTransformer(instance, loaderContext, getProgram), | ||
| ...(transformers?.before ?? []), | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 
 | ||
| ]; | ||
| } | ||
| return transformers; | ||
| } | ||
|  | ||
| function getScriptRegexp(instance: TSInstance) { | ||
|  | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -33,23 +33,21 @@ import { | |
| useCaseSensitiveFileNames, | ||
| } from './utils'; | ||
|  | ||
| function makeResolversAndModuleResolutionHost( | ||
| scriptRegex: RegExp, | ||
| loader: webpack.LoaderContext<LoaderOptions>, | ||
| export function makeModuleResolutionHost( | ||
| instance: TSInstance, | ||
| fileExists: (fileName: string) => boolean, | ||
| enableFileCaching: boolean | ||
| ) { | ||
| const { | ||
| compiler, | ||
| compilerOptions, | ||
| appendTsTsxSuffixesIfRequired, | ||
| loaderOptions: { | ||
| resolveModuleName: customResolveModuleName, | ||
| resolveTypeReferenceDirective: customResolveTypeReferenceDirective, | ||
| }, | ||
| } = instance; | ||
|  | ||
| loader: webpack.LoaderContext<LoaderOptions>, | ||
| enableFileCaching: boolean, | ||
| fileExists?: (fileName: string) => boolean | ||
| ): ModuleResolutionHostMayBeCacheable { | ||
| const { files, otherFiles, compiler, compilerOptions, filePathKeyMapper } = instance; | ||
| fileExists ??= (fileName: string) => { | ||
| const filePathKey = filePathKeyMapper(fileName); | ||
| return ( | ||
| files.has(filePathKey) || | ||
| otherFiles.has(filePathKey) || | ||
| compiler.sys.fileExists(fileName) | ||
| ); | ||
| }; | ||
| const newLine = | ||
| compilerOptions.newLine === constants.CarriageReturnLineFeedCode | ||
| ? constants.CarriageReturnLineFeed | ||
|  | @@ -60,9 +58,6 @@ function makeResolversAndModuleResolutionHost( | |
| // loader.context seems to work fine on Linux / Mac regardless causes problems for @types resolution on Windows for TypeScript < 2.3 | ||
| const getCurrentDirectory = () => loader.context; | ||
|  | ||
| // make a (sync) resolver that follows webpack's rules | ||
| const resolveSync = makeResolver(loader._compiler!.options); | ||
|  | ||
| const moduleResolutionHost: ModuleResolutionHostMayBeCacheable = { | ||
| trace: logData => instance.log.log(logData), | ||
| fileExists, | ||
|  | @@ -79,21 +74,22 @@ function makeResolversAndModuleResolutionHost( | |
| getDefaultLibFileName: options => compiler.getDefaultLibFilePath(options), | ||
| }; | ||
|  | ||
|  | ||
| if (enableFileCaching) { | ||
| addCache(moduleResolutionHost); | ||
| } | ||
|  | ||
| return makeResolvers( | ||
| compiler, | ||
| compilerOptions, | ||
| moduleResolutionHost, | ||
| customResolveTypeReferenceDirective, | ||
| customResolveModuleName, | ||
| resolveSync, | ||
| appendTsTsxSuffixesIfRequired, | ||
| scriptRegex, | ||
| instance | ||
| ); | ||
|  | ||
| if (!instance.moduleResolutionCache && !instance.loaderOptions.resolveModuleName) { | ||
| instance.moduleResolutionCache = createModuleResolutionCache( | ||
| instance, | ||
| moduleResolutionHost | ||
| ); | ||
| } | ||
|  | ||
| instance.moduleResolutionHost = moduleResolutionHost; | ||
|  | ||
| return moduleResolutionHost; | ||
|  | ||
| function readFile( | ||
| filePath: string, | ||
|  | @@ -134,6 +130,42 @@ function makeResolversAndModuleResolutionHost( | |
| } | ||
| } | ||
|  | ||
| function makeResolversAndModuleResolutionHost( | ||
| scriptRegex: RegExp, | ||
| loader: webpack.LoaderContext<LoaderOptions>, | ||
| instance: TSInstance, | ||
| fileExists: (fileName: string) => boolean, | ||
| enableFileCaching: boolean | ||
| ) { | ||
| const { | ||
| compiler, | ||
| compilerOptions, | ||
| appendTsTsxSuffixesIfRequired, | ||
| loaderOptions: { | ||
| resolveModuleName: customResolveModuleName, | ||
| resolveTypeReferenceDirective: customResolveTypeReferenceDirective, | ||
| }, | ||
| } = instance; | ||
|  | ||
|  | ||
|  | ||
| // make a (sync) resolver that follows webpack's rules | ||
| const resolveSync = makeResolver(loader._compiler!.options); | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you really need this? Based on logic it is just an error - https://github.com/TypeStrong/ts-loader/blob/main/src/resolver.ts#L11 Webpack has great API for resolving, you can use (just an example of code, we should have two resolvers for cjs and esm in the real life): And hide a lot of options for basic  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just moved code, not code I wrote | ||
| const moduleResolutionHost = makeModuleResolutionHost(instance, loader, enableFileCaching, fileExists); | ||
|  | ||
| return makeResolvers( | ||
| compiler, | ||
| compilerOptions, | ||
| moduleResolutionHost, | ||
| customResolveTypeReferenceDirective, | ||
| customResolveModuleName, | ||
| resolveSync, | ||
| appendTsTsxSuffixesIfRequired, | ||
| scriptRegex, | ||
| instance | ||
| ); | ||
| } | ||
|  | ||
| /** | ||
| * Create the TypeScript language service | ||
| */ | ||
|  | @@ -768,7 +800,7 @@ export function makeSolutionBuilderHost( | |
| ); | ||
|  | ||
| // Keeps track of the various `typescript.CustomTransformers` for each program that is created. | ||
| const customTransformers = new Map<string, typescript.CustomTransformers>(); | ||
| const customTransformers = new Map<string, typescript.CustomTransformers | undefined>(); | ||
|  | ||
| // let lastBuilderProgram: typescript.CreateProgram | undefined = undefined; | ||
| const solutionBuilderHost: SolutionBuilderWithWatchHost = { | ||
|  | @@ -802,7 +834,7 @@ export function makeSolutionBuilderHost( | |
| if (typeof project === "string") { | ||
| // Custom transformers need a reference to the `typescript.Program`, that reference is | ||
| // unavailable during the the `getCustomTransformers` callback below. | ||
| const transformers = getCustomTransformers(instance.loaderOptions, result.getProgram(), result.getProgram); | ||
| const transformers = getCustomTransformers(instance, loader, result.getProgram(), result.getProgram); | ||
| customTransformers.set(project, transformers); | ||
| } | ||
| } | ||
|  | @@ -1335,12 +1367,6 @@ function makeResolveModuleName( | |
| instance: TSInstance | ||
| ): ResolveModuleName { | ||
| if (customResolveModuleName === undefined) { | ||
| if (!instance.moduleResolutionCache) { | ||
| instance.moduleResolutionCache = createModuleResolutionCache( | ||
| instance, | ||
| moduleResolutionHost | ||
| ); | ||
| } | ||
| return ( | ||
| moduleName, | ||
| containingFileName, | ||
|  | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| import assert from "assert"; | ||
| import externalLib from "./lib/externalLib.js"; | ||
| assert.deepStrictEqual(externalLib, { default: {} }); | ||
| console.log("PASS"); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"I got 99 problems but EcmaScript modules ain't one"