1
1
'use strict' ;
2
+
2
3
const {
3
4
RegExpPrototypeExec,
4
5
ObjectPrototypeHasOwnProperty,
5
6
PromisePrototypeThen,
6
7
PromiseResolve,
8
+ StringPrototypeIncludes,
7
9
StringPrototypeCharCodeAt,
8
10
StringPrototypeSlice,
9
11
} = primordials ;
10
12
const { basename, relative } = require ( 'path' ) ;
11
13
const { getOptionValue } = require ( 'internal/options' ) ;
12
14
const {
13
15
extensionFormatMap,
16
+ getFormatOfExtensionlessFile,
14
17
getLegacyExtensionFormat,
15
18
mimeToFormat,
16
19
} = require ( 'internal/modules/esm/formats' ) ;
@@ -19,6 +22,9 @@ const experimentalNetworkImports =
19
22
getOptionValue ( '--experimental-network-imports' ) ;
20
23
const experimentalSpecifierResolution =
21
24
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' ;
22
28
const { getPackageType, getPackageScopeConfig } = require ( 'internal/modules/esm/resolve' ) ;
23
29
const { fileURLToPath } = require ( 'internal/url' ) ;
24
30
const { ERR_UNKNOWN_FILE_EXTENSION } = require ( 'internal/errors' ) . codes ;
@@ -69,6 +75,18 @@ function extname(url) {
69
75
return '' ;
70
76
}
71
77
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
+
72
90
/**
73
91
* @param {URL } url
74
92
* @param {{parentURL: string} } context
@@ -77,8 +95,37 @@ function extname(url) {
77
95
*/
78
96
function getFileProtocolModuleFormat ( url , context , ignoreErrors ) {
79
97
const ext = extname ( url ) ;
98
+
80
99
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
+ }
82
129
}
83
130
84
131
const format = extensionFormatMap [ ext ] ;
@@ -93,12 +140,10 @@ function getFileProtocolModuleFormat(url, context, ignoreErrors) {
93
140
const config = getPackageScopeConfig ( url ) ;
94
141
const fileBasename = basename ( filepath ) ;
95
142
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"}}` ;
102
147
}
103
148
throw new ERR_UNKNOWN_FILE_EXTENSION ( ext , filepath , suggestion ) ;
104
149
}
0 commit comments