11'use strict' ;
2+
23const {
34 RegExpPrototypeExec,
45 ObjectPrototypeHasOwnProperty,
56 PromisePrototypeThen,
67 PromiseResolve,
8+ StringPrototypeIncludes,
79 StringPrototypeCharCodeAt,
810 StringPrototypeSlice,
911} = primordials ;
1012const { basename, relative } = require ( 'path' ) ;
1113const { getOptionValue } = require ( 'internal/options' ) ;
1214const {
1315 extensionFormatMap,
16+ getFormatOfExtensionlessFile,
1417 getLegacyExtensionFormat,
1518 mimeToFormat,
1619} = require ( 'internal/modules/esm/formats' ) ;
@@ -19,6 +22,9 @@ const experimentalNetworkImports =
1922 getOptionValue ( '--experimental-network-imports' ) ;
2023const experimentalSpecifierResolution =
2124 getOptionValue ( '--experimental-specifier-resolution' ) ;
25+ const defaultTypeFlag = getOptionValue ( '--experimental-default-type' ) ;
26+ // The next line is where we flip the default to ES modules someday.
27+ const defaultType = defaultTypeFlag === 'module' ? 'module' : 'commonjs' ;
2228const { getPackageType, getPackageScopeConfig } = require ( 'internal/modules/esm/resolve' ) ;
2329const { fileURLToPath } = require ( 'internal/url' ) ;
2430const { ERR_UNKNOWN_FILE_EXTENSION } = require ( 'internal/errors' ) . codes ;
@@ -69,6 +75,18 @@ function extname(url) {
6975 return '' ;
7076}
7177
78+ /**
79+ * Determine whether the given file URL is under a `node_modules` folder.
80+ * This function assumes that the input has already been verified to be a `file:` URL,
81+ * and is a file rather than a folder.
82+ * @param {URL } url
83+ */
84+ function underNodeModules ( url ) {
85+ if ( url . protocol !== 'file:' ) { return false ; } // We determine module types for other protocols based on MIME header
86+
87+ return StringPrototypeIncludes ( url . pathname , '/node_modules/' ) ;
88+ }
89+
7290/**
7391 * @param {URL } url
7492 * @param {{parentURL: string} } context
@@ -77,8 +95,37 @@ function extname(url) {
7795 */
7896function getFileProtocolModuleFormat ( url , context , ignoreErrors ) {
7997 const ext = extname ( url ) ;
98+
8099 if ( ext === '.js' ) {
81- return getPackageType ( url ) === 'module' ? 'module' : 'commonjs' ;
100+ const packageType = getPackageType ( url ) ;
101+ if ( packageType !== 'none' ) {
102+ return packageType ;
103+ }
104+ // The controlling `package.json` file has no `type` field.
105+ if ( defaultType === 'module' ) {
106+ // An exception to the type flag making ESM the default everywhere is that package scopes under `node_modules`
107+ // should retain the assumption that a lack of a `type` field means CommonJS.
108+ return underNodeModules ( url ) ? 'commonjs' : 'module' ;
109+ }
110+ return 'commonjs' ;
111+ }
112+
113+ if ( ext === '' ) {
114+ const packageType = getPackageType ( url ) ;
115+ if ( defaultType === 'commonjs' ) { // Legacy behavior
116+ if ( packageType === 'none' || packageType === 'commonjs' ) {
117+ return 'commonjs' ;
118+ }
119+ // If package type is `module`, fall through to the error case below
120+ } else { // Else defaultType === 'module'
121+ if ( underNodeModules ( url ) ) { // Exception for package scopes under `node_modules`
122+ return 'commonjs' ;
123+ }
124+ if ( packageType === 'none' || packageType === 'module' ) {
125+ return getFormatOfExtensionlessFile ( url ) ;
126+ } // Else packageType === 'commonjs'
127+ return 'commonjs' ;
128+ }
82129 }
83130
84131 const format = extensionFormatMap [ ext ] ;
@@ -93,12 +140,10 @@ function getFileProtocolModuleFormat(url, context, ignoreErrors) {
93140 const config = getPackageScopeConfig ( url ) ;
94141 const fileBasename = basename ( filepath ) ;
95142 const relativePath = StringPrototypeSlice ( relative ( config . pjsonPath , filepath ) , 1 ) ;
96- suggestion = 'Loading extensionless files is not supported inside of ' +
97- '"type":"module" package.json contexts. The package.json file ' +
98- `${ config . pjsonPath } caused this "type":"module" context. Try ` +
99- `changing ${ filepath } to have a file extension. Note the "bin" ` +
100- 'field of package.json can point to a file with an extension, for example ' +
101- `{"type":"module","bin":{"${ fileBasename } ":"${ relativePath } .js"}}` ;
143+ suggestion = 'Loading extensionless files is not supported inside of "type":"module" package.json contexts ' +
144+ `without --experimental-default-type=module. The package.json file ${ config . pjsonPath } caused this "type":"module" ` +
145+ `context. Try changing ${ filepath } to have a file extension. Note the "bin" field of package.json can point ` +
146+ `to a file with an extension, for example {"type":"module","bin":{"${ fileBasename } ":"${ relativePath } .js"}}` ;
102147 }
103148 throw new ERR_UNKNOWN_FILE_EXTENSION ( ext , filepath , suggestion ) ;
104149 }
0 commit comments