babel-loader 9.2.1 → 10.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- > This README is for babel-loader v8/v9 with Babel v7
1
+ > This README is for babel-loader v8/v9/v10 with Babel v7
2
2
  > If you are using legacy Babel v6, see the [7.x branch](https://github.com/babel/babel-loader/tree/7.x) docs
3
3
 
4
4
  [![NPM Status](https://img.shields.io/npm/v/babel-loader.svg?style=flat)](https://www.npmjs.com/package/babel-loader)
@@ -6,7 +6,7 @@
6
6
 
7
7
  <div align="center">
8
8
  <a href="https://github.com/babel/babel">
9
- <img src="https://rawgit.com/babel/logo/master/babel.svg" alt="Babel logo" width="200" height="200">
9
+ <img src="https://raw.githubusercontent.com/babel/logo/master/babel.svg" alt="Babel logo" width="200" height="200">
10
10
  </a>
11
11
  <a href="https://github.com/webpack/webpack">
12
12
  <img src="https://webpack.js.org/assets/icon-square-big.svg" alt="webpack logo" width="200" height="200">
@@ -15,7 +15,7 @@
15
15
 
16
16
  <h1 align="center">Babel Loader</h1>
17
17
 
18
- This package allows transpiling JavaScript files using [Babel](https://github.com/babel/babel) and [webpack](https://github.com/webpack/webpack).
18
+ This package allows transpiling JavaScript files using [Babel](https://github.com/babel/babel) together with [webpack](https://github.com/webpack/webpack) or [Rspack](https://github.com/web-infra-dev/rspack).
19
19
 
20
20
  **Note**: Issues with the output should be reported on the Babel [Issues](https://github.com/babel/babel/issues) tracker.
21
21
 
@@ -25,6 +25,7 @@ This package allows transpiling JavaScript files using [Babel](https://github.co
25
25
  > |:-:|:-:|:-:|:-:|
26
26
  > | 8.x | 4.x or 5.x | 7.x | >= 8.9 |
27
27
  > | 9.x | 5.x | ^7.12.0 | >= 14.15.0 |
28
+ > | 10.x | ^5.61.0 | ^7.12.0 \|\| ^8.0.0-alpha | ^18.20.0 \|\| ^20.10.0 \|\| >=22.0.0` |
28
29
 
29
30
 
30
31
  ```bash
@@ -46,8 +47,9 @@ module: {
46
47
  use: {
47
48
  loader: 'babel-loader',
48
49
  options: {
50
+ targets: "defaults",
49
51
  presets: [
50
- ['@babel/preset-env', { targets: "defaults" }]
52
+ ['@babel/preset-env']
51
53
  ]
52
54
  }
53
55
  }
@@ -71,10 +73,11 @@ module: {
71
73
  use: {
72
74
  loader: 'babel-loader',
73
75
  options: {
76
+ targets: "defaults",
74
77
  presets: [
75
- ['@babel/preset-env', { targets: "defaults" }]
78
+ ['@babel/preset-env']
76
79
  ],
77
- plugins: ['@babel/plugin-proposal-class-properties']
80
+ plugins: ['@babel/plugin-proposal-decorators', { version: "2023-11" }]
78
81
  }
79
82
  }
80
83
  }
@@ -88,20 +91,30 @@ This loader also supports the following loader-specific option:
88
91
 
89
92
  * `cacheDirectory`: Default `false`. When set, the given directory will be used to cache the results of the loader. Future webpack builds will attempt to read from the cache to avoid needing to run the potentially expensive Babel recompilation process on each run. If the value is set to `true` in options (`{cacheDirectory: true}`), the loader will use the default cache directory in `node_modules/.cache/babel-loader` or fallback to the default OS temporary file directory if no `node_modules` folder could be found in any root directory.
90
93
 
91
- * `cacheIdentifier`: Default is a string composed by
92
- - the `@babel/core`'s version and the `babel-loader`'s version
93
- - the [merged](https://babeljs.io/docs/configuration#how-babel-merges-config-items) [Babel config](https://babeljs.io/docs/config-files), including options passed to `babel-loader` and the contents of `babel.config.js` or `.babelrc` file if they exist
94
- - the value of the environment variable `BABEL_ENV` with a fallback to the `NODE_ENV` environment variable.
95
- This can be set to a custom value to force cache busting if the identifier changes.
94
+ * `cacheIdentifier`: Default is a string composed by the `@babel/core`'s version and the `babel-loader`'s version. The final cache id will be determined by the input file path, the [merged](https://babeljs.io/docs/configuration#how-babel-merges-config-items) Babel config via `Babel.loadPartialConfigAsync` and the `cacheIdentifier`. The merged Babel config will be determined by the `babel.config.js` or `.babelrc` file if they exist, or the value of the environment variable `BABEL_ENV` and `NODE_ENV`. `cacheIdentifier` can be set to a custom value to force cache busting if the identifier changes.
96
95
 
97
96
  * `cacheCompression`: Default `true`. When set, each Babel transform output will be compressed with Gzip. If you want to opt-out of cache compression, set it to `false` -- your project may benefit from this if it transpiles thousands of files.
98
97
 
99
98
  * `customize`: Default `null`. The path of a module that exports a `custom` callback [like the one that you'd pass to `.custom()`](#customized-loader). Since you already have to make a new file to use this, it is recommended that you instead use `.custom` to create a wrapper loader. Only use this if you _must_ continue using `babel-loader` directly, but still want to customize.
100
99
 
101
- * `metadataSubscribers`: Default `[]`. Takes an array of context function names. E.g. if you passed ['myMetadataPlugin'], you'd assign a subscriber function to `context.myMetadataPlugin` within your webpack plugin's hooks & that function will be called with `metadata`.
100
+ * `metadataSubscribers`: Default `[]`. Takes an array of context function names. E.g. if you passed ['myMetadataPlugin'], you'd assign a subscriber function to `context.myMetadataPlugin` within your webpack plugin's hooks & that function will be called with `metadata`. See [`./test/metadata.test.js`](./test/metadata.test.js) for an example.
102
101
 
103
102
  ## Troubleshooting
104
103
 
104
+ ### Enable debug mode logging
105
+
106
+ Specify the webpack option [`stats.loggingDebug`](https://webpack.js.org/configuration/stats/#statsloggingdebug) to output verbose debug logs.
107
+
108
+ ```js
109
+ // webpack.config.js
110
+ module.exports = {
111
+ // ...
112
+ stats: {
113
+ loggingDebug: ["babel-loader"]
114
+ }
115
+ }
116
+ ```
117
+
105
118
  ### babel-loader is slow!
106
119
 
107
120
  Make sure you are transforming as few files as possible. Because you are probably matching `/\.m?js$/`, you might be transforming the `node_modules` folder or other unwanted source.
@@ -291,10 +304,6 @@ For example, to change the environment targets passed to `@babel/preset-env` bas
291
304
 
292
305
  module.exports = api => {
293
306
  return {
294
- plugins: [
295
- "@babel/plugin-proposal-nullish-coalescing-operator",
296
- "@babel/plugin-proposal-optional-chaining"
297
- ],
298
307
  presets: [
299
308
  [
300
309
  "@babel/preset-env",
package/lib/Error.js CHANGED
@@ -1,4 +1,14 @@
1
+ // @ts-check
1
2
  const STRIP_FILENAME_RE = /^[^:]+: /;
3
+
4
+ /**
5
+ * @typedef { Error & { hideStack?: boolean, codeFrame?: string } } BabelLoaderError
6
+ */
7
+ /**
8
+ * Format the error for display.
9
+ * @param {BabelLoaderError} err
10
+ * @returns {BabelLoaderError}
11
+ */
2
12
  const format = err => {
3
13
  if (err instanceof SyntaxError) {
4
14
  err.name = "SyntaxError";
@@ -12,6 +22,9 @@ const format = err => {
12
22
  return err;
13
23
  };
14
24
  class LoaderError extends Error {
25
+ /**
26
+ * @param {BabelLoaderError} err
27
+ */
15
28
  constructor(err) {
16
29
  super();
17
30
  const {
package/lib/cache.js CHANGED
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  /**
2
3
  * Filesystem Cache
3
4
  *
@@ -7,10 +8,10 @@
7
8
  * @see https://github.com/babel/babel-loader/issues/34
8
9
  * @see https://github.com/babel/babel-loader/pull/41
9
10
  */
11
+ const nodeModule = require("node:module");
10
12
  const os = require("os");
11
13
  const path = require("path");
12
14
  const zlib = require("zlib");
13
- const crypto = require("crypto");
14
15
  const {
15
16
  promisify
16
17
  } = require("util");
@@ -19,26 +20,53 @@ const {
19
20
  writeFile,
20
21
  mkdir
21
22
  } = require("fs/promises");
22
- const findCacheDirP = import("find-cache-dir");
23
+ const {
24
+ sync: findUpSync
25
+ } = require("find-up");
26
+ const {
27
+ env
28
+ } = process;
23
29
  const transform = require("./transform");
24
- // Lazily instantiated when needed
30
+ const serialize = require("./serialize");
31
+ /**
32
+ * @typedef {object} FileSystemInfoEntry
33
+ * @property {number} safeTime
34
+ * @property {number} timestamp
35
+ */
36
+ /**
37
+ * @typedef {object} WebpackLogger
38
+ * @property {function(string): void} debug
39
+ * @property {function(string): void} info
40
+ * @property {function(string): void} warn
41
+ * @property {function(string): void} error
42
+ */
43
+ /**
44
+ * @typedef {object} WebpackHash
45
+ * @property {(data: string | Buffer, inputEncoding?: string) => WebpackHash} update
46
+ * @property {(encoding?: string) => string | Buffer} digest
47
+ */
48
+
49
+ /**
50
+ * @type {string | null}
51
+ */
25
52
  let defaultCacheDirectory = null;
26
- let hashType = "sha256";
27
- // use md5 hashing if sha256 is not available
28
- try {
29
- crypto.createHash(hashType);
30
- } catch {
31
- hashType = "md5";
32
- }
33
53
  const gunzip = promisify(zlib.gunzip);
34
54
  const gzip = promisify(zlib.gzip);
55
+ const findRootPackageJSON = () => {
56
+ if (nodeModule.findPackageJSON) {
57
+ return nodeModule.findPackageJSON("..", __filename);
58
+ } else {
59
+ // todo: remove this fallback when dropping support for Node.js < 22.14
60
+ return findUpSync("package.json");
61
+ }
62
+ };
35
63
 
36
64
  /**
37
65
  * Read the contents from the compressed file.
38
66
  *
39
67
  * @async
40
- * @params {String} filename
41
- * @params {Boolean} compress
68
+ * @param {string} filename
69
+ * @param {boolean} compress
42
70
  */
43
71
  const read = async function (filename, compress) {
44
72
  const data = await readFile(filename + (compress ? ".gz" : ""));
@@ -48,11 +76,10 @@ const read = async function (filename, compress) {
48
76
 
49
77
  /**
50
78
  * Write contents into a compressed file.
51
- *
52
79
  * @async
53
- * @params {String} filename
54
- * @params {Boolean} compress
55
- * @params {String} result
80
+ * @param {string} filename
81
+ * @param {boolean} compress
82
+ * @param {any} result
56
83
  */
57
84
  const write = async function (filename, compress, result) {
58
85
  const content = JSON.stringify(result);
@@ -62,28 +89,74 @@ const write = async function (filename, compress, result) {
62
89
 
63
90
  /**
64
91
  * Build the filename for the cached file
65
- *
66
- * @params {String} source File source code
67
- * @params {Object} options Options used
68
- *
69
- * @return {String}
92
+ * @param {string} source File source code
93
+ * @param {string} identifier Unique identifier to bust cache
94
+ * @param {Object} options Options used
95
+ * @param {WebpackHash} hash Hash function returned by `LoaderContext.utils.createHash`
96
+ * @return {string}
70
97
  */
71
- const filename = function (source, identifier, options) {
72
- const hash = crypto.createHash(hashType);
73
- const contents = JSON.stringify({
74
- source,
75
- options,
76
- identifier
77
- });
78
- hash.update(contents);
98
+ const filename = function (source, identifier, options, hash) {
99
+ hash.update(serialize([options, source, identifier]));
79
100
  return hash.digest("hex") + ".json";
80
101
  };
81
102
 
103
+ /**
104
+ * Add timestamps to external dependencies.
105
+ * @async
106
+ * @param {import("./transform").TransformResult["externalDependencies"]} externalDependencies
107
+ * @param {(filename: string) => Promise<FileSystemInfoEntry>} getFileTimestamp
108
+ */
109
+ const addTimestamps = async function (externalDependencies, getFileTimestamp) {
110
+ for (const depAndEmptyTimestamp of externalDependencies) {
111
+ try {
112
+ const [dep] = depAndEmptyTimestamp;
113
+ const {
114
+ timestamp
115
+ } = await getFileTimestamp(dep);
116
+ depAndEmptyTimestamp.push(timestamp);
117
+ } catch {
118
+ // ignore errors if timestamp is not available
119
+ }
120
+ }
121
+ };
122
+
123
+ /**
124
+ * Check if any external dependencies have been modified.
125
+ * @async
126
+ * @param {import("./transform").TransformResult["externalDependencies"]} externalDepsWithTimestamp
127
+ * @param {(filename: string) => Promise<FileSystemInfoEntry>} getFileTimestamp
128
+ * @returns {Promise<boolean>}
129
+ */
130
+ const areExternalDependenciesModified = async function (externalDepsWithTimestamp, getFileTimestamp) {
131
+ for (const depAndTimestamp of externalDepsWithTimestamp) {
132
+ const [dep, timestamp] = depAndTimestamp;
133
+ let newTimestamp;
134
+ try {
135
+ newTimestamp = (await getFileTimestamp(dep)).timestamp;
136
+ } catch {
137
+ return true;
138
+ }
139
+ if (timestamp !== newTimestamp) {
140
+ return true;
141
+ }
142
+ }
143
+ return false;
144
+ };
145
+
82
146
  /**
83
147
  * Handle the cache
84
- *
85
- * @params {String} directory
86
- * @params {Object} params
148
+ * @async
149
+ * @param {string} directory
150
+ * @param {Object} params
151
+ * @param {string} params.source The source code to transform.
152
+ * @param {import(".").NormalizedOptions} [params.options] Options used for transformation.
153
+ * @param {string} params.cacheIdentifier Unique identifier to bust cache.
154
+ * @param {string} [params.cacheDirectory] Directory to store cached files.
155
+ * @param {boolean} [params.cacheCompression] Whether to compress cached files.
156
+ * @param {WebpackHash} params.hash Hash function to use for the cache filename.
157
+ * @param {(filename: string) => Promise<FileSystemInfoEntry>} params.getFileTimestamp - Function to get file timestamps.
158
+ * @param {WebpackLogger} params.logger
159
+ * @returns {Promise<null | import("./transform").TransformResult>}
87
160
  */
88
161
  const handleCache = async function (directory, params) {
89
162
  const {
@@ -92,16 +165,23 @@ const handleCache = async function (directory, params) {
92
165
  cacheIdentifier,
93
166
  cacheDirectory,
94
167
  cacheCompression,
168
+ hash,
169
+ getFileTimestamp,
95
170
  logger
96
171
  } = params;
97
- const file = path.join(directory, filename(source, cacheIdentifier, options));
172
+ const file = path.join(directory, filename(source, cacheIdentifier, options, hash));
98
173
  try {
99
174
  // No errors mean that the file was previously cached
100
175
  // we just need to return it
101
176
  logger.debug(`reading cache file '${file}'`);
102
- return await read(file, cacheCompression);
177
+ const result = await read(file, cacheCompression);
178
+ if (!(await areExternalDependenciesModified(result.externalDependencies, getFileTimestamp))) {
179
+ logger.debug(`validated cache file '${file}'`);
180
+ return result;
181
+ }
182
+ logger.debug(`discarded cache file '${file}' due to changes in external dependencies`);
103
183
  } catch {
104
- // conitnue if cache can't be read
184
+ // continue if cache can't be read
105
185
  logger.debug(`discarded cache as it can not be read`);
106
186
  }
107
187
  const fallback = typeof cacheDirectory !== "string" && directory !== os.tmpdir();
@@ -124,34 +204,37 @@ const handleCache = async function (directory, params) {
124
204
  // return it to the user asap and write it in cache
125
205
  logger.debug(`applying Babel transform`);
126
206
  const result = await transform(source, options);
127
-
128
- // Do not cache if there are external dependencies,
129
- // since they might change and we cannot control it.
130
- if (!result.externalDependencies.length) {
131
- try {
132
- logger.debug(`writing result to cache file '${file}'`);
133
- await write(file, cacheCompression, result);
134
- } catch (err) {
135
- if (fallback) {
136
- // Fallback to tmpdir if node_modules folder not writable
137
- return handleCache(os.tmpdir(), params);
138
- }
139
- throw err;
207
+ if (!result) {
208
+ logger.debug(`no result from Babel transform, skipping cache write`);
209
+ return null;
210
+ }
211
+ await addTimestamps(result.externalDependencies, getFileTimestamp);
212
+ try {
213
+ logger.debug(`writing result to cache file '${file}'`);
214
+ await write(file, cacheCompression, result);
215
+ } catch (err) {
216
+ if (fallback) {
217
+ // Fallback to tmpdir if node_modules folder not writable
218
+ return handleCache(os.tmpdir(), params);
140
219
  }
220
+ throw err;
141
221
  }
142
222
  return result;
143
223
  };
144
224
 
145
225
  /**
146
226
  * Retrieve file from cache, or create a new one for future reads
147
- *
148
227
  * @async
149
- * @param {Object} params
150
- * @param {String} params.cacheDirectory Directory to store cached files
151
- * @param {String} params.cacheIdentifier Unique identifier to bust cache
152
- * @param {Boolean} params.cacheCompression Whether compressing cached files
153
- * @param {String} params.source Original contents of the file to be cached
154
- * @param {Object} params.options Options to be given to the transform fn
228
+ * @param {object} params
229
+ * @param {string} params.cacheDirectory Directory to store cached files.
230
+ * @param {string} params.cacheIdentifier Unique identifier to bust cache.
231
+ * @param {boolean} params.cacheCompression Whether compressing cached files.
232
+ * @param {string} params.source Original contents of the file to be cached.
233
+ * @param {import(".").NormalizedOptions} params.options Options to be given to the transform function.
234
+ * @param {function} params.transform Transform function to apply to the file.
235
+ * @param {WebpackHash} params.hash Hash function to use for the cache filename.
236
+ * @param {function(string): Promise<FileSystemInfoEntry>} params.getFileTimestamp Function to get file timestamps.
237
+ * @param {WebpackLogger} params.logger Logger instance.
155
238
  *
156
239
  * @example
157
240
  *
@@ -167,20 +250,29 @@ const handleCache = async function (directory, params) {
167
250
  * });
168
251
  */
169
252
 
170
- module.exports = async function (params) {
253
+ module.exports = async function cache(params) {
171
254
  let directory;
172
255
  if (typeof params.cacheDirectory === "string") {
173
256
  directory = params.cacheDirectory;
174
257
  } else {
175
- if (defaultCacheDirectory === null) {
176
- const {
177
- default: findCacheDir
178
- } = await findCacheDirP;
179
- defaultCacheDirectory = findCacheDir({
180
- name: "babel-loader"
181
- }) || os.tmpdir();
182
- }
258
+ defaultCacheDirectory ??= findCacheDir("babel-loader");
183
259
  directory = defaultCacheDirectory;
184
260
  }
185
261
  return await handleCache(directory, params);
186
- };
262
+ };
263
+
264
+ /**
265
+ * Find the cache directory for babel-loader.
266
+ * @param {string} name "babel-loader"
267
+ * @returns {string}
268
+ */
269
+ function findCacheDir(name) {
270
+ if (env.CACHE_DIR && !["true", "false", "1", "0"].includes(env.CACHE_DIR)) {
271
+ return path.join(env.CACHE_DIR, name);
272
+ }
273
+ const rootPkgJSONPath = findRootPackageJSON();
274
+ if (rootPkgJSONPath) {
275
+ return path.join(path.dirname(rootPkgJSONPath), "node_modules", ".cache", name);
276
+ }
277
+ return os.tmpdir();
278
+ }
package/lib/index.js CHANGED
@@ -1,3 +1,37 @@
1
+ // @ts-check
2
+ /**
3
+ * @typedef {object} LoaderOnlyOptions
4
+ * @property {string} [cacheDirectory] Directory to store cached files.
5
+ * @property {string} [cacheIdentifier] Unique identifier to bust cache.
6
+ * @property {boolean} [cacheCompression] Whether to compress cached files.
7
+ * @property {string} [customize] The absolute path of a file that exports a BabelLoaderWrapper.
8
+ * @property {Array<string>} [metadataSubscribers] Names of subscribers registered in the loader context.
9
+ */
10
+
11
+ /**
12
+ * @typedef {import("webpack").LoaderContext<LoaderOptions>} BabelLoaderContext
13
+ * @typedef {string} BabelLoaderSource Parameters<import("webpack").LoaderDefinitionFunction>[0]
14
+ * @typedef {string} BabelLoaderInputSourceMap Parameters<import("webpack").LoaderDefinitionFunction>[1]
15
+ *
16
+ * @todo Consider exporting these types from @babel/core
17
+ * @typedef {Awaited<ReturnType<import("@babel/core").loadPartialConfigAsync>>} PartialConfig
18
+ * @typedef {PartialConfig['options']} NormalizedOptions
19
+ */
20
+
21
+ /**
22
+ * @typedef {(babel: typeof import("@babel/core")) => BabelOverrideHooks} BabelLoaderWrapper
23
+ * @typedef {object} BabelOverrideHooks
24
+ * @property {(this: BabelLoaderContext, loaderOptions: LoaderOptions, params: { source: BabelLoaderSource, map: BabelLoaderInputSourceMap }) => Promise<{ custom: any, loader: LoaderOptions }>} customOptions
25
+ * @property {(this: BabelLoaderContext, config: PartialConfig, params: { source: BabelLoaderSource, map: BabelLoaderInputSourceMap, customOptions: any }) => Promise<PartialConfig['options']>} config
26
+ * @property {(this: BabelLoaderContext, result: import("./transform").TransformResult, params: { source: BabelLoaderSource, map: BabelLoaderInputSourceMap, customOptions: any, config: PartialConfig, options: PartialConfig['options'] }) => Promise<import("./transform").TransformResult>} result
27
+ */
28
+ /**
29
+ * @typedef {import("@babel/core").InputOptions & LoaderOnlyOptions} LoaderOptions
30
+ */
31
+
32
+ /**
33
+ * @type {import("@babel/core")}
34
+ */
1
35
  let babel;
2
36
  try {
3
37
  babel = require("@babel/core");
@@ -19,39 +53,66 @@ const {
19
53
  const cache = require("./cache");
20
54
  const transform = require("./transform");
21
55
  const injectCaller = require("./injectCaller");
22
- const schema = require("./schema");
56
+ const schema = require("./schema.json");
23
57
  const {
24
58
  isAbsolute
25
59
  } = require("path");
26
- const validateOptions = require("schema-utils").validate;
60
+ const {
61
+ promisify
62
+ } = require("util");
63
+
64
+ /**
65
+ * Invoke a metadata subscriber registered in the loader context.
66
+ * @param {string} subscriber
67
+ * @param {unknown} metadata
68
+ * @param {import("webpack").LoaderContext<LoaderOptions>} context
69
+ */
27
70
  function subscribe(subscriber, metadata, context) {
71
+ // @ts-expect-error subscriber is a custom function
28
72
  if (context[subscriber]) {
73
+ // @ts-expect-error subscriber is a custom function
29
74
  context[subscriber](metadata);
30
75
  }
31
76
  }
32
77
  module.exports = makeLoader();
33
78
  module.exports.custom = makeLoader;
79
+
80
+ /**
81
+ * @param {BabelLoaderWrapper} [callback]
82
+ */
34
83
  function makeLoader(callback) {
35
84
  const overrides = callback ? callback(babel) : undefined;
36
- return function (source, inputSourceMap) {
85
+
86
+ /**
87
+ * @this {BabelLoaderContext}
88
+ * @param {BabelLoaderSource} source
89
+ * @param {BabelLoaderInputSourceMap} inputSourceMap
90
+ */
91
+ const webpackLoader = function (source, inputSourceMap) {
37
92
  // Make the loader async
38
93
  const callback = this.async();
39
- loader.call(this, source, inputSourceMap, overrides).then(args => callback(null, ...args), err => callback(err));
94
+ loader.call(this, source, inputSourceMap, overrides).then(
95
+ // @ts-expect-error (FixMe): Argument of type 'string | EncodedSourceMap' is not assignable to parameter of type 'string | Buffer<ArrayBufferLike>'.
96
+ args => callback(null, ...args), err => callback(err));
40
97
  };
98
+ return webpackLoader;
41
99
  }
100
+
101
+ /**
102
+ * Babel loader
103
+ * @this {BabelLoaderContext}
104
+ * @param {BabelLoaderSource} source The source code to transform
105
+ * @param {BabelLoaderInputSourceMap} inputSourceMap
106
+ * @param {BabelOverrideHooks} overrides
107
+ * @returns
108
+ */
42
109
  async function loader(source, inputSourceMap, overrides) {
43
110
  const filename = this.resourcePath;
44
- const logger = typeof this.getLogger === "function" ? this.getLogger("babel-loader") : {
45
- debug: () => {}
46
- };
47
- let loaderOptions = this.getOptions();
48
- validateOptions(schema, loaderOptions, {
49
- name: "Babel loader"
50
- });
111
+ const logger = this.getLogger("babel-loader");
112
+
113
+ // @ts-expect-error TS does not treat schema.json/properties/cacheDirectory/type as a constant string literal
114
+ let loaderOptions = this.getOptions(schema);
51
115
  if (loaderOptions.customize != null) {
52
- if (typeof loaderOptions.customize !== "string") {
53
- throw new Error("Customized loaders must be implemented as standalone modules.");
54
- }
55
116
  if (!isAbsolute(loaderOptions.customize)) {
56
117
  throw new Error("Customized loaders must be passed as absolute paths, since " + "babel-loader has no way to know what they would be relative to.");
57
118
  }
@@ -80,10 +141,10 @@ async function loader(source, inputSourceMap, overrides) {
80
141
 
81
142
  // Deprecation handling
82
143
  if ("forceEnv" in loaderOptions) {
83
- console.warn("The option `forceEnv` has been removed in favor of `envName` in Babel 7.");
144
+ this.emitWarning(new Error("The option `forceEnv` has been removed in favor of `envName` in Babel 7."));
84
145
  }
85
146
  if (typeof loaderOptions.babelrc === "string") {
86
- console.warn("The option `babelrc` should not be set to a string anymore in the babel-loader config. " + "Please update your configuration and set `babelrc` to true or false.\n" + "If you want to specify a specific babel config file to inherit config from " + "please use the `extends` option.\nFor more information about this options see " + "https://babeljs.io/docs/core-packages/#options");
147
+ this.emitWarning(new Error("The option `babelrc` should not be set to a string anymore in the babel-loader config. " + "Please update your configuration and set `babelrc` to true or false.\n" + "If you want to specify a specific babel config file to inherit config from " + "please use the `extends` option.\nFor more information about this options see " + "https://babeljs.io/docs/#options"));
87
148
  }
88
149
  logger.debug("normalizing loader options");
89
150
  // Standardize on 'sourceMaps' as the key passed through to Webpack, so that
@@ -135,17 +196,26 @@ async function loader(source, inputSourceMap, overrides) {
135
196
  }
136
197
  const {
137
198
  cacheDirectory = null,
138
- cacheIdentifier = JSON.stringify({
139
- options,
140
- "@babel/core": transform.version,
141
- "@babel/loader": version
142
- }),
199
+ cacheIdentifier = "core" + transform.version + "," + "loader" + version,
143
200
  cacheCompression = true,
144
201
  metadataSubscribers = []
145
202
  } = loaderOptions;
203
+
204
+ /**
205
+ * @type {import("./transform").TransformResult}
206
+ */
146
207
  let result;
147
208
  if (cacheDirectory) {
148
209
  logger.debug("cache is enabled");
210
+ const getFileTimestamp = promisify(
211
+ /**
212
+ * @param {string} path
213
+ * @param {(err: import("webpack").WebpackError | null, fileTimestamp: import("./cache").FileSystemInfoEntry) => void} cb
214
+ */
215
+ (path, cb) => {
216
+ this._compilation.fileSystemInfo.getFileTimestamp(path, cb);
217
+ });
218
+ const hash = this.utils.createHash(this._compilation.outputOptions.hashFunction);
149
219
  result = await cache({
150
220
  source,
151
221
  options,
@@ -153,6 +223,8 @@ async function loader(source, inputSourceMap, overrides) {
153
223
  cacheDirectory,
154
224
  cacheIdentifier,
155
225
  cacheCompression,
226
+ hash,
227
+ getFileTimestamp,
156
228
  logger
157
229
  });
158
230
  } else {
@@ -180,7 +252,7 @@ async function loader(source, inputSourceMap, overrides) {
180
252
  metadata,
181
253
  externalDependencies
182
254
  } = result;
183
- externalDependencies?.forEach(dep => {
255
+ externalDependencies?.forEach(([dep]) => {
184
256
  this.addDependency(dep);
185
257
  logger.debug(`added '${dep}' to webpack dependencies`);
186
258
  });
@@ -1,5 +1,13 @@
1
+ // @ts-check
2
+ /**
3
+ * Inject babel-loader caller information into the Babel options.
4
+ * @param {import("@babel/core").InputOptions} opts
5
+ * @param {string} target
6
+ * @returns {import("@babel/core").InputOptions}
7
+ */
1
8
  module.exports = function injectCaller(opts, target) {
2
- return Object.assign({}, opts, {
9
+ return {
10
+ ...opts,
3
11
  caller: Object.assign({
4
12
  name: "babel-loader",
5
13
  // Provide plugins with insight into webpack target.
@@ -13,5 +21,5 @@ module.exports = function injectCaller(opts, target) {
13
21
  // flag isn't enabled.
14
22
  supportsTopLevelAwait: true
15
23
  }, opts.caller)
16
- });
24
+ };
17
25
  };
package/lib/schema.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
+ "title": "Babel Loader options",
2
3
  "type": "object",
3
4
  "properties": {
4
5
  "cacheDirectory": {
5
- "oneOf": [
6
+ "anyOf": [
6
7
  {
7
8
  "type": "boolean"
8
9
  },
@@ -20,8 +21,18 @@
20
21
  "default": true
21
22
  },
22
23
  "customize": {
23
- "type": "string",
24
+ "anyOf": [
25
+ {
26
+ "type": "null"
27
+ },
28
+ {
29
+ "type": "string"
30
+ }
31
+ ],
24
32
  "default": null
33
+ },
34
+ "metadataSubscribers": {
35
+ "type": "array"
25
36
  }
26
37
  },
27
38
  "additionalProperties": true
@@ -0,0 +1,95 @@
1
+ // @ts-check
2
+ var objToString = Object.prototype.toString;
3
+ var objKeys = Object.getOwnPropertyNames;
4
+
5
+ /**
6
+ * A custom Babel options serializer
7
+ *
8
+ * Intentional deviation from JSON.stringify:
9
+ * 1. Object properties are sorted before serializing
10
+ * 2. The output is NOT a valid JSON: e.g.
11
+ * The output does not enquote strings, which means a JSON-like string '{"a":1}'
12
+ * will share the same result with an JS object { a: 1 }. This is not an issue
13
+ * for Babel options, but it can not be used for general serialization purpose
14
+ * 3. Only 20% slower than the native JSON.stringify on V8
15
+ *
16
+ * This function is a fork from https://github.com/nickyout/fast-stable-stringify
17
+ * @param {unknown} val Babel options
18
+ * @param {boolean} isArrayProp
19
+ * @returns serialized Babel options
20
+ */
21
+ function serialize(val, isArrayProp) {
22
+ var i, max, str, keys, key, propVal, toStr;
23
+ if (val === true) {
24
+ return "!0";
25
+ }
26
+ if (val === false) {
27
+ return "!1";
28
+ }
29
+ switch (typeof val) {
30
+ case "object":
31
+ if (val === null) {
32
+ return null;
33
+ // @ts-expect-error
34
+ } else if (val.toJSON && typeof val.toJSON === "function") {
35
+ // @ts-expect-error toJSON has been checked above
36
+ return serialize(val.toJSON(), isArrayProp);
37
+ } else {
38
+ toStr = objToString.call(val);
39
+ if (toStr === "[object Array]") {
40
+ str = "[";
41
+ // @ts-expect-error val is an array
42
+ max = val.length - 1;
43
+ for (i = 0; i < max; i++) {
44
+ // @ts-expect-error val is an array
45
+ str += serialize(val[i], true) + ",";
46
+ }
47
+ if (max > -1) {
48
+ // @ts-expect-error val is an array
49
+ str += serialize(val[i], true);
50
+ }
51
+ return str + "]";
52
+ } else if (toStr === "[object Object]") {
53
+ // only object is left
54
+ keys = objKeys(val).sort();
55
+ max = keys.length;
56
+ str = "{";
57
+ i = 0;
58
+ while (i < max) {
59
+ key = keys[i];
60
+ // @ts-expect-error key must index val
61
+ propVal = serialize(val[key], false);
62
+ if (propVal !== undefined) {
63
+ if (str) {
64
+ str += ",";
65
+ }
66
+ str += '"' + key + '":' + propVal;
67
+ }
68
+ i++;
69
+ }
70
+ return str + "}";
71
+ } else {
72
+ return JSON.stringify(val);
73
+ }
74
+ }
75
+ case "function":
76
+ case "undefined":
77
+ return isArrayProp ? null : undefined;
78
+ case "string":
79
+ return val;
80
+ default:
81
+ // @ts-expect-error val must be a number because of the toJSON check above
82
+ return isFinite(val) ? val : null;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * @param {unknown} val
88
+ * @returns {string | undefined}
89
+ */
90
+ module.exports = function (val) {
91
+ var returnVal = serialize(val, false);
92
+ if (returnVal !== undefined) {
93
+ return "" + returnVal;
94
+ }
95
+ };
package/lib/transform.js CHANGED
@@ -1,13 +1,31 @@
1
+ // @ts-check
1
2
  const babel = require("@babel/core");
2
3
  const {
3
4
  promisify
4
5
  } = require("util");
5
6
  const LoaderError = require("./Error");
6
- const transform = promisify(babel.transform);
7
- module.exports = async function (source, options) {
7
+ const babelTransform = babel.transformAsync ?? promisify(babel.transform);
8
+ /**
9
+ * @typedef {Object} AmendedTransformResult
10
+ * @property {[string, number?][]} externalDependencies
11
+ */
12
+ /**
13
+ * @typedef {Omit<import("@babel/core").FileResult, "externalDependencies" | "options"> & AmendedTransformResult} TransformResult
14
+ */
15
+ /**
16
+ * Transform the source code using Babel.
17
+ * @async
18
+ * @param {string} source The source code to transform.
19
+ * @param {import("@babel/core").InputOptions} options The Babel options.
20
+ * @returns {Promise<null | TransformResult>} The transformed result or null if no transformation is needed.
21
+ */
22
+ module.exports = async function transform(source, options) {
23
+ /**
24
+ * @type {import("@babel/core").FileResult}
25
+ */
8
26
  let result;
9
27
  try {
10
- result = await transform(source, options);
28
+ result = await babelTransform(source, options);
11
29
  } catch (err) {
12
30
  throw err.message && err.codeFrame ? new LoaderError(err) : err;
13
31
  }
@@ -15,7 +33,7 @@ module.exports = async function (source, options) {
15
33
 
16
34
  // We don't return the full result here because some entries are not
17
35
  // really serializable. For a full list of properties see here:
18
- // https://github.com/babel/babel/blob/main/packages/babel-core/src/transformation/index.js
36
+ // https://github.com/babel/babel/blob/main/packages/babel-core/src/transformation/index.ts
19
37
  // For discussion on this topic see here:
20
38
  // https://github.com/babel/babel-loader/pull/629
21
39
  const {
@@ -36,7 +54,12 @@ module.exports = async function (source, options) {
36
54
  metadata,
37
55
  sourceType,
38
56
  // Convert it from a Set to an Array to make it JSON-serializable.
39
- externalDependencies: Array.from(externalDependencies || [])
57
+ externalDependencies: Array.from(externalDependencies || [],
58
+ /**
59
+ * @param {string} dep
60
+ * @returns {[string, number?]}
61
+ */
62
+ dep => [dep]).sort()
40
63
  };
41
64
  };
42
65
  module.exports.version = babel.version;
package/package.json CHANGED
@@ -1,38 +1,46 @@
1
1
  {
2
2
  "name": "babel-loader",
3
- "version": "9.2.1",
3
+ "version": "10.1.0",
4
4
  "description": "babel module loader for webpack",
5
5
  "files": [
6
6
  "lib"
7
7
  ],
8
8
  "main": "lib/index.js",
9
9
  "engines": {
10
- "node": ">= 14.15.0"
10
+ "node": "^18.20.0 || ^20.10.0 || >=22.0.0"
11
11
  },
12
12
  "dependencies": {
13
- "find-cache-dir": "^4.0.0",
14
- "schema-utils": "^4.0.0"
13
+ "find-up": "^5.0.0"
15
14
  },
16
15
  "peerDependencies": {
17
- "@babel/core": "^7.12.0",
18
- "webpack": ">=5"
16
+ "@babel/core": "^7.12.0 || ^8.0.0-beta.1",
17
+ "@rspack/core": "^1.0.0 || ^2.0.0-0",
18
+ "webpack": ">=5.61.0"
19
+ },
20
+ "peerDependenciesMeta": {
21
+ "@rspack/core": {
22
+ "optional": true
23
+ },
24
+ "webpack": {
25
+ "optional": true
26
+ }
19
27
  },
20
28
  "devDependencies": {
21
- "@ava/babel": "^1.0.1",
22
- "@babel/cli": "^7.23.0",
23
- "@babel/core": "^7.23.3",
24
- "@babel/eslint-parser": "^7.23.3",
25
- "@babel/preset-env": "^7.23.3",
26
- "ava": "^3.13.0",
27
- "c8": "^8.0.0",
29
+ "@babel/cli": "^8.0.0-beta.1",
30
+ "@babel/core": "^8.0.0-beta.1",
31
+ "@babel/eslint-parser": "^8.0.0-beta.1",
32
+ "@babel/preset-env": "^8.0.0-beta.1",
33
+ "@rspack/core": "^1.7.5",
34
+ "c8": "^10.1.2",
28
35
  "eslint": "^9.6.0",
29
36
  "eslint-config-prettier": "^9.1.0",
30
37
  "eslint-plugin-prettier": "^5.1.3",
31
38
  "globals": "^15.8.0",
32
- "husky": "^8.0.3",
33
- "lint-staged": "^13.2.3",
39
+ "husky": "^9.1.5",
40
+ "lint-staged": "^15.2.9",
34
41
  "prettier": "^3.0.0",
35
- "webpack": "^5.89.0"
42
+ "typescript": "^5.8.3",
43
+ "webpack": "^5.93.0"
36
44
  },
37
45
  "scripts": {
38
46
  "clean": "node ./scripts/rimraf.mjs lib",
@@ -41,9 +49,9 @@
41
49
  "lint": "eslint src test",
42
50
  "precommit": "lint-staged",
43
51
  "prepublish": "yarn run clean && yarn run build",
44
- "preversion": "yarn run test",
52
+ "preversion": "yarn tsc && yarn run test",
45
53
  "test": "yarn run lint && yarn run build --source-maps && c8 yarn run test-only",
46
- "test-only": "ava"
54
+ "test-only": "node --test test/**/*.test.js"
47
55
  },
48
56
  "resolutions": {
49
57
  "minipass": "6.0.2"
@@ -78,18 +86,6 @@
78
86
  "sourceMap": false,
79
87
  "instrument": false
80
88
  },
81
- "ava": {
82
- "files": [
83
- "test/**/*.test.js",
84
- "!test/fixtures/**/*",
85
- "!test/helpers/**/*"
86
- ],
87
- "babel": {
88
- "compileAsTests": [
89
- "test/helpers/**/*"
90
- ]
91
- }
92
- },
93
89
  "lint-staged": {
94
90
  "scripts/*.js": [
95
91
  "prettier --trailing-comma es5 --write",
@@ -113,4 +109,4 @@
113
109
  ]
114
110
  },
115
111
  "packageManager": "yarn@3.6.4"
116
- }
112
+ }
package/CHANGELOG.md DELETED
@@ -1,169 +0,0 @@
1
- # Changelog
2
-
3
- For changes in version v7.0.0 and up please go to [release](https://github.com/babel/babel-loader/releases)
4
-
5
- # Old Changelog
6
-
7
- ## v6.4.1
8
-
9
- ### 🐛 Bug Fix
10
-
11
- - Fixed reset of BABEL_ENV when options.forceEnv is used (#420) @nikopavlica
12
-
13
- ## v6.4.0
14
-
15
- ### 🚀 New Feature
16
-
17
- - added metadata passing from babel to webpack, which is currently used by react-intl (#398) @Ognian
18
-
19
- ## v6.3.2
20
-
21
- ### 😢 Regression
22
-
23
- - `forceEnv` was interfering with regular environment handling
24
-
25
- ## v6.3.1
26
-
27
- ### 🐛 Bug Fix
28
-
29
- - The new `forceEnv` options wasn't working as expected (#379) @chrisvasz
30
-
31
- ## v6.3.0
32
-
33
- ### 🚀 New Feature
34
-
35
- - Add new config option `forceEnv` (#368) @moimael
36
-
37
- Allow to override BABEL_ENV/NODE_ENV at loader-level. Useful for isomorphic applications which have separate babel config for client and server.
38
-
39
- ### 🐛 Bug Fix
40
-
41
- - Update loader-utils dependency to ^0.2.16 to fix compatibility with webpack 2 (#371) @leonaves
42
-
43
- ### 💅 Polish
44
-
45
- - Improve FS caching to do less sync calls which improves performance slightly (#375) @akx
46
-
47
- ## v6.2.10
48
-
49
- Support for webpack 2.2-rc has been added in this release
50
-
51
- ### 🐛 Bug Fix
52
-
53
- - If cache directory not writable, try to fallback to tmpdir before failing
54
-
55
- ## v6.2.9
56
-
57
- ### 😢 Regression
58
-
59
- Source maps on windows did not work correctly with v6.2.8.
60
- Thanks @josephst
61
-
62
- ### 🏠 Internal
63
-
64
- - Add AppVeyor to run tests on windows @danez
65
- - Fix tests on windows (#343) @danez
66
-
67
- ## v6.2.8
68
-
69
- ### 🐛 Bug Fix
70
-
71
- - gzipped files should have `.gz` as the extension, not `.gzip` (#326) @bjornstar
72
- - fix options.sourceFileName gennerate bug (#260) @creeperyang
73
-
74
- ### 📝 Documentation
75
-
76
- - Update README docs for cacheDirectory's actual behaviour (#245) @sohkai
77
- - updates docs re: transform-runtime (#197) @gbrassey
78
-
79
- ### 🏠 Internal
80
-
81
- - Use eslint and nyc (#321) @danez
82
- - Adjust travis config (#320) @danez
83
- - Use babel to compile babel-loader (#319) @danez
84
-
85
- ## v6.2.7
86
-
87
- ### 😢 Regression
88
-
89
- Fallback to `os.tmpdir()` if no cachedir found (#318) (fixes #317) @danez
90
-
91
- Fixes an issue with v6.2.6 when using `babel-loader` as a global package.
92
-
93
- ## v6.2.6
94
-
95
- ### 🐛 Bug Fix
96
-
97
- - Use standard cache dir as default `cacheDirectory` (#301) @fson
98
-
99
- Use the common cache directory, `./node_modules/.cache/babel-loader`, as the default cache directory (when the cacheDirectory setting is enabled).
100
-
101
- ```js
102
- query: {
103
- cacheDirectory: true
104
- }
105
- ```
106
-
107
- ## v6.2.5
108
-
109
- - Don't show the call stack for a Babel error (such as when you have a syntax error)
110
-
111
- <img width="415" alt="screenshot 2016-08-15 15 24 37" src="https://cloud.githubusercontent.com/assets/30594/17664401/727ba098-62fc-11e6-9f12-42da0cf47f14.png">
112
-
113
- - resolve the .babelrc relative to the file path rather than the cwd (current working directory).
114
-
115
- * fix: more concise formatting for Babel errors (#287) (Andrey Popp)
116
- * fix(resolve-rc): resolve-rc relative file path (#253) (Luke Page)
117
- * add babel-core and preset-2015 to dev dependencies (#273) (timse)
118
- * chore(docs): add issue and pr templates (#280) (Joshua Wiens)
119
- * chore(docs): fix badge formatting (Joshua Wiens)
120
- * chore(ci): expand travis testing (#278) (Joshua Wiens)
121
- * Update README: add env vars to cacheIdentifier (#267) (Dominik Ferber)
122
- * add npm badge [skip ci] (Henry Zhu)
123
- * update [skip ci] (Henry Zhu)
124
- * remove jsx references as well [skip ci] (Henry Zhu)
125
- * Save the transform to devDependencies (Ray Booysen)
126
- * Remove 'react' preset (Jake Rios)
127
- * Removed babel-preset-react from README.md (Ben Stephenson)
128
-
129
- ## v6.2.4
130
- * change allowed peer deps (all webpack 2 beta versions)
131
-
132
- ## v6.2.3
133
- * change allowed peer deps (2.0.7-beta)
134
-
135
- ## v6.2.2
136
- * Update peerDependencies to accept webpack 2 [#208](https://github.com/babel/babel-loader/pull/208)
137
- * Remove duplicated dependencies
138
-
139
- ## v6.2.0
140
- * Pass true filenames [#106](https://github.com/babel/babel-loader/issues/106)
141
- * Remove babel-core from devDependencies
142
-
143
- ## v6.1.0
144
-
145
- * Merge [PR #146](https://github.com/babel/babel-loader/pull/146) Set source file name relative to options.sourceRoot
146
- * Merge [PR #136](https://github.com/babel/babel-loader/pull/136) use container-based infrastructure for faster build
147
- * Merge [PR #121](https://github.com/babel/babel-loader/pull/121) Make babelrc configurable
148
- * Merge [PR #113](https://github.com/babel/babel-loader/pull/113) Include BABEL_ENV || NODE_ENV in cacheIdentifier
149
-
150
- ## v6.0.1
151
-
152
- * Update to babel v6.
153
-
154
- ## v5.3.1
155
-
156
- * Merge [PR #85](https://github.com/babel/babel-loader/pull/85) - Don't override sourcemap if sourcesContent already exists.
157
-
158
-
159
- ## v5.3.1
160
-
161
- * Merge [PR #82](https://github.com/babel/babel-loader/pull/82) - Fallback global options to empty object to avoid conflicts with object-assign polyfill.
162
-
163
- ## v5.3.0
164
-
165
- * Merge [PR #79](https://github.com/babel/babel-loader/pull/79) - This should allow babel-loader to work with [enhanced-require](https://github.com/webpack/enhanced-require).
166
-
167
- ## v5.2.0
168
-
169
- * Include `.babelrc` file into the `cacheIdentifier` if it exists