diff --git a/website/docs/en/api/loader-api/context.mdx b/website/docs/en/api/loader-api/context.mdx index 1648019d2eb..4a50c6e9a8a 100644 --- a/website/docs/en/api/loader-api/context.mdx +++ b/website/docs/en/api/loader-api/context.mdx @@ -8,52 +8,87 @@ The loader context represents the properties that are available inside of a load ## this.addContextDependency() +- **Type:** + ```ts function addContextDependency(directory: string): void; ``` Add the directory as a dependency for the loader results so that any changes to the files in the directory can be listened to. +For example, adding `src/static` as a dependency. When the files in the `src/static` directory change, it will trigger a rebuild. + +```js title="loader.js" +const path = require('node:path'); + +module.exports = function loader(source) { + this.addContextDependency(path.resolve(this.rootContext, 'src/static')); + return source; +}; +``` + ## this.addDependency() +- **Type:** + ```ts function addDependency(file: string): void; ``` Add a file as a dependency on the loader results so that any changes to them can be listened to. For example, `sass-loader`, `less-loader` use this trick to recompile when the imported style files change. -## this.dependency() +```js title="loader.js" +const path = require('node:path'); -```ts -function dependency(file: string): void; +module.exports = function loader(source) { + this.addDependency(path.resolve(this.rootContext, 'src/styles/foo.scss')); + return source; +}; ``` -Alias of `this.addDependency()`. - ## this.addMissingDependency() +- **Type:** + ```ts function addMissingDependency(file: string): void; ``` -Add a non-existent file as a dependency on the loader results to make them listenable. +Add a currently non-existent file as a dependency of the loader result, so that its creation and any changes can be listened. For example, when a new file is created at that path, it will trigger a rebuild. + +```js title="loader.js" +const path = require('node:path'); + +module.exports = function loader(source) { + this.addMissingDependency( + path.resolve(this.rootContext, 'src/dynamic-file.json'), + ); + return source; +}; +``` ## this.async() -Tells Rspack that this loader will be called asynchronously. Returns `this.callback`. +- **Type:** `() => LoaderContextCallback` + +Tells Rspack that this loader will be called asynchronously. Returns [this.callback](#thiscallback). ## this.cacheable() -A function that sets the cacheable flag: +- **Type:** ```ts function cacheable(flag: boolean = true): void; ``` +A function that sets the cacheable flag: + By default, the processing results of the loader are marked as cacheable. Calling this method and passing `false` turns off the loader's ability to cache processing results. ## this.callback() +- **Type:** + ```ts function callback( err: Error | null, @@ -79,6 +114,8 @@ The `sourceMap` and `meta` parameters are optional. If they are not passed, the ## this.clearDependencies() +- **Type:** + ```ts function clearDependencies(): void; ``` @@ -87,21 +124,50 @@ Removes all dependencies of the loader result. ## this.context -The directory where the current module is located. +- **Type:** `string | null` + +The directory path of the currently processed module, which changes with the location of each processed module. + +For example, if the loader is processing `/project/src/components/Button.js`, then the value of `this.context` would be `/project/src/components`. + +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.context); // '/project/src/components' + return source; +}; +``` + +If the module being processed is not from the file system, such as a virtual module, then the value of `this.context` is `null`. ## this.data +- **Type:** `unknown` + A data object shared between the pitch and the normal phase. +## this.dependency() + +- **Type:** + +```ts +function dependency(file: string): void; +``` + +Alias of [this.addDependency()](#thisadddependency). + ## this.emitError() +- **Type:** + ```ts function emitError(error: Error): void; ``` Emit an error. Unlike `throw` and `this.callback(err)` in the loader, it does not mark the current module as a compilation failure, it just adds an error to Rspack's Compilation and displays it on the command line at the end of this compilation. -## this.emitWarning(warning: Error) +## this.emitWarning() + +- **Type:** ```ts function emitWarning(warning: Error): void; @@ -111,22 +177,115 @@ Emit a warning. ## this.emitFile() +- **Type:** + ```ts function emitFile( name: string, - content: Buffer | string, - sourceMap: SourceMap, + content: string | Buffer, + sourceMap?: string, + assetInfo?: JsAssetInfo, ): void; ``` -Emit a file. +Emit a new file. This method allows you to create new files during the loader execution. + +- Basic example: + +```js title="loader.js" +module.exports = function loader(source) { + // Emit a new file that will be output as `foo.js` in the output directory + this.emitFile('foo.js', 'console.log("Hello, world!");'); + return source; +}; +``` + +- Example with asset info: + +```js title="loader.js" +module.exports = function loader(source) { + this.emitFile( + 'foo.js', + 'console.log("Hello, world!");', + undefined, // no sourcemap + { + sourceFilename: this.resourcePath, + }, + ); + + return source; +}; +``` + +## this.getOptions() + +- **Type:** + +```ts +function getOptions(schema?: any): OptionsType; +``` + +Get the options passed in by the loader's user. + +For example: + +```js title="rspack.config.js" +module.exports = { + module: { + rules: [ + { + test: /\.txt$/, + use: { + loader: './my-loader.js', + options: { + foo: 'bar', + }, + }, + }, + ], + }, +}; +``` + +In `my-loader.js` get the options passed in: + +```js title="my-loader.js" +module.exports = function myLoader(source) { + const options = this.getOptions(); + console.log(options); // { foo: 'bar' } + return source; +}; +``` + +In TypeScript, you can set the options type through the generic of `LoaderContext`. + +```ts title="my-loader.ts" +import type { LoaderContext } from '@rspack/core'; + +type MyLoaderOptions = { + foo: string; +}; + +export default function myLoader( + this: LoaderContext, + source: string, +) { + const options = this.getOptions(); + console.log(options); // { foo: 'bar' } + return source; +} +``` -## this.getOptions(schema) +:::tip +The parameter `schema` is optional and will not be used in Rspack. -Extracts the given loader option, accepting an optional JSON schema as an argument. +To provide the best performance, Rspack does not perform the schema validation. If your loader requires schema validation, please call [scheme-utils](https://github.com/webpack/scheme-utils) or other schema validation libraries. +::: ## this.getResolve() +- **Type:** + ```ts function getResolve(options: ResolveOptions): resolve; ``` @@ -135,6 +294,8 @@ Create a resolver like `this.resolve`. ## this.importModule() +- **Type:** + ```ts interface ImportModuleOptions { /** @@ -172,7 +333,7 @@ Compile and execute a module at the build time. This is an alternative lightweig const path = require('node:path'); module.exports = async function loader(source) { - const modulePath = path.resolve(__dirname, 'some-module.ts'); + const modulePath = path.resolve(this.rootContext, 'some-module.ts'); const moduleExports = await this.importModule(modulePath, { // optional options }); @@ -189,7 +350,7 @@ const path = require('node:path'); module.exports = function loader(source) { const callback = this.async(); - const modulePath = path.resolve(__dirname, 'some-module.ts'); + const modulePath = path.resolve(this.rootContext, 'some-module.ts'); this.importModule( modulePath, @@ -209,6 +370,8 @@ module.exports = function loader(source) { ## this.resolve() +- **Type:** + ```ts function resolve( context: string, @@ -225,40 +388,107 @@ Resolve a request. ## this.mode +- **Type:** `Mode` + The value of [`mode`](/config/mode) is read when Rspack is run. The possible values are: `'production'`, `'development'`, `'none'` +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.mode); // 'production' or other values + return source; +}; +``` + ## this.target +- **Type:** `Target` + The value of [`target`](/config/target) is read when Rspack is run. +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.target); // 'web' or other values + return source; +}; +``` + ## this.resource +- **Type:** `string` + The path string of the current module. For example `'/abc/resource.js?query#hash'`. +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.resource); // '/abc/resource.js?query#hash' + return source; +}; +``` + ## this.resourcePath +- **Type:** `string` + The path string of the current module, excluding the query and fragment parameters. For example `'/abc/resource.js?query#hash'` in `'/abc/resource.js'`. +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.resourcePath); // '/abc/resource.js' + return source; +}; +``` + ## this.resourceQuery +- **Type:** `string` + The query parameter for the path string of the current module. For example `'?query'` in `'/abc/resource.js?query#hash'`. +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.resourceQuery); // '?query' + return source; +}; +``` + ## this.resourceFragment +- **Type:** `string` + The fragment parameter of the current module's path string. For example `'#hash'` in `'/abc/resource.js?query#hash'`. +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.resourceFragment); // '#hash' + return source; +}; +``` + ## this.rootContext -The directory where the project is configured in config +- **Type:** `string` + +The base path configured in Rspack config via [context](/config/context). + +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.rootContext); // /path/to/project + return source; +}; +``` ## this.sourceMap +- **Type:** `boolean` + Whether a source map should be generated. ## this.getLogger() +- **Type:** + ```ts function getLogger(name?: string): void; ``` diff --git a/website/docs/zh/api/loader-api/context.mdx b/website/docs/zh/api/loader-api/context.mdx index fc2d43482ac..88e3f2f9807 100644 --- a/website/docs/zh/api/loader-api/context.mdx +++ b/website/docs/zh/api/loader-api/context.mdx @@ -8,42 +8,75 @@ Loader 上下文表示 loader 内部可用的属性,这些属性在 loader 中 ## this.addContextDependency() +- **类型:** + ```ts function addContextDependency(directory: string): void; ``` 添加目录作为 loader 结果的依赖,使目录中文件的任何变化可以被监听到。 +例如,添加 `src/static` 目录作为依赖,当目录中的文件发生变化时,会触发重新构建。 + +```js title="loader.js" +const path = require('node:path'); + +module.exports = function loader(source) { + this.addContextDependency(path.resolve(this.rootContext, 'src/static')); + return source; +}; +``` + ## this.addDependency() +- **类型:** + ```ts function addDependency(file: string): void; ``` 添加一个文件作为 loader 结果的依赖,使它们的任何变化可以被监听到。例如,`sass-loader`、`less-loader` 就使用了这个技巧,当导入的样式文件发生变化时就会重新编译。 -## this.dependency() +```js title="loader.js" +const path = require('node:path'); -```ts -function dependency(file: string): void; +module.exports = function loader(source) { + this.addDependency(path.resolve(this.rootContext, 'src/styles/foo.scss')); + return source; +}; ``` -`this.addDependency()` 的别名。 - ## this.addMissingDependency() +- **类型:** + ```ts function addMissingDependency(file: string): void; ``` -添加一个不存在的文件作为 loader 结果的依赖项,以使它们可监听。 +添加一个当前不存在的文件作为 loader 结果的依赖,使它们的创建和任何变化可以被监听到。例如,当该路径下新建了文件时,会触发重新构建。 + +```js title="loader.js" +const path = require('node:path'); + +module.exports = function loader(source) { + this.addMissingDependency( + path.resolve(this.rootContext, 'src/dynamic-file.json'), + ); + return source; +}; +``` ## this.async() -告诉 Rspack 这个 loader 将会异步被调用。返回 `this.callback`。 +- **类型:** `() => LoaderContextCallback` + +告诉 Rspack 这个 loader 将会异步被调用。返回值为 [this.callback](#thiscallback)。 ## this.cacheable() +- **类型:** + ```ts function cacheable(flag: boolean = true): void; ``` @@ -52,6 +85,8 @@ function cacheable(flag: boolean = true): void; ## this.clearDependencies() +- **类型:** + ```ts function clearDependencies(): void; ``` @@ -60,6 +95,8 @@ function clearDependencies(): void; ## this.callback() +- **类型:** + ```ts function callback( err: Error | null, @@ -82,14 +119,41 @@ function callback( ## this.context -当前模块所在的目录。 +- **类型:** `string | null` + +当前被处理的模块所在的目录路径,会随着每个被处理的模块的位置而变化。 + +例如,如果 loader 处理的是 `/project/src/components/Button.js`,那么 `this.context` 的值就是 `/project/src/components`。 + +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.context); // '/project/src/components' + return source; +}; +``` + +如果被处理的是模块不是来自文件系统,例如虚拟模块,那么 `this.context` 的值为 `null`。 ## this.data +- **类型:** `unknown` + 用于在 pitch 和 normal 阶段之间共享数据。 +## this.dependency() + +- **类型:** + +```ts +function dependency(file: string): void; +``` + +[this.addDependency()](#thisadddependency) 的别名。 + ## this.emitError() +- **类型:** + ```ts function emitError(err: Error): void; ``` @@ -98,6 +162,8 @@ function emitError(err: Error): void; ## this.emitWarning() +- **类型:** + ```ts function emitWarning(warning: Error): void; ``` @@ -106,22 +172,115 @@ function emitWarning(warning: Error): void; ## this.emitFile() +- **类型:** + ```ts function emitFile( name: string, - content: Buffer | string, - sourceMap: SourceMap, + content: string | Buffer, + sourceMap?: string, + assetInfo?: JsAssetInfo, ): void; ``` -产生一个文件。 +输出一个新文件。这个方法允许你在 loader 执行过程中创建新的文件。 + +- 基础示例: + +```js title="loader.js" +module.exports = function loader(source) { + // 输出一个新文件,该文件将在产物目录中输出为 `foo.js` + this.emitFile('foo.js', 'console.log("Hello, world!");'); + return source; +}; +``` + +- 带有 asset info 的示例: + +```js title="loader.js" +module.exports = function loader(source) { + this.emitFile( + 'foo.js', + 'console.log("Hello, world!");', + undefined, // no sourcemap + { + sourceFilename: this.resourcePath, + }, + ); + + return source; +}; +``` -## this.getOptions(schema) +## this.getOptions() -提取给定的 loader 选项,接受一个可选的 JSON schema 作为参数。 +- **类型:** + +```ts +function getOptions(schema?: any): OptionsType; +``` + +获取 loader 的使用者传入的选项。 + +例如: + +```js title="rspack.config.js" +module.exports = { + module: { + rules: [ + { + test: /\.txt$/, + use: { + loader: './my-loader.js', + options: { + foo: 'bar', + }, + }, + }, + ], + }, +}; +``` + +在 `my-loader.js` 中获取传入的选项: + +```js title="my-loader.js" +module.exports = function myLoader(source) { + const options = this.getOptions(); + console.log(options); // { foo: 'bar' } + return source; +}; +``` + +在 TypeScript 中,你可以通过 `LoaderContext` 的泛型来设置 options 的类型。 + +```ts title="my-loader.ts" +import type { LoaderContext } from '@rspack/core'; + +type MyLoaderOptions = { + foo: string; +}; + +export default function myLoader( + this: LoaderContext, + source: string, +) { + const options = this.getOptions(); + console.log(options); // { foo: 'bar' } + return source; +} +``` + +:::tip +参数 `schema` 是可选的,在 Rspack 中不会被使用。 + +为了提供最佳性能,Rspack 不会执行 schema 验证。如果你的 loader 需要 schema 验证,请自行调用 [scheme-utils](https://github.com/webpack/scheme-utils) 或其他 schema 验证库。 +::: ## this.getResolve() +- **类型:** + ```ts function getResolve(options: ResolveOptions): resolve; ``` @@ -130,6 +289,8 @@ function getResolve(options: ResolveOptions): resolve; ## this.importModule() +- **类型:** + ```ts interface ImportModuleOptions { /** @@ -167,7 +328,7 @@ function importModule( const path = require('node:path'); module.exports = async function loader(source) { - const modulePath = path.resolve(__dirname, 'some-module.ts'); + const modulePath = path.resolve(this.rootContext, 'some-module.ts'); const moduleExports = await this.importModule(modulePath, { // 可选参数 }); @@ -184,7 +345,7 @@ const path = require('node:path'); module.exports = function loader(source) { const callback = this.async(); - const modulePath = path.resolve(__dirname, 'some-module.ts'); + const modulePath = path.resolve(this.rootContext, 'some-module.ts'); this.importModule( modulePath, @@ -204,6 +365,8 @@ module.exports = function loader(source) { ## this.resolve() +- **类型:** + ```ts function resolve( context: string, @@ -220,40 +383,105 @@ function resolve( ## this.mode -当 Rspack 运行时读取 [mode](/config/mode) 的值 +- **类型:** `Mode` -可能的值为:`'production'`、`'development'`、`'none'` +当 Rspack 运行时读取 [mode](/config/mode) 的值,可能的值为:`'production'`、`'development'`、`'none'`。 + +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.mode); // 'production' or other values + return source; +}; +``` ## this.target -当 Rspack 运行时读取 [target](/config/target) 的值 +- **类型:** `Target` + +当 Rspack 运行时读取 [target](/config/target) 的值。 + +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.target); // 'web' or other values + return source; +}; +``` ## this.resource +- **类型:** `string` + 当前模块的路径字符串。比如 `'/abc/resource.js?query#hash'`。 +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.resource); // '/abc/resource.js?query#hash' + return source; +}; +``` + ## this.resourcePath +- **类型:** `string` + 当前模块的路径字符串,不包括 query 和 fragment 参数。比如 `'/abc/resource.js?query#hash'` 中的 `'/abc/resource.js'`。 +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.resourcePath); // '/abc/resource.js' + return source; +}; +``` + ## this.resourceQuery +- **类型:** `string` + 当前模块的路径字符串的 query 参数。比如 `'/abc/resource.js?query#hash'` 中的 `'?query'`。 +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.resourceQuery); // '?query' + return source; +}; +``` + ## this.resourceFragment +- **类型:** `string` + 当前模块的路径字符串的 fragment 参数。比如 `'/abc/resource.js?query#hash'` 中的 `'#hash'`。 +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.resourceFragment); // '#hash' + return source; +}; +``` + ## this.rootContext -config 中配置的项目所在的目录 +- **类型:** `string` + +Rspack config 中通过 [context](/config/context) 配置的基础路径。 + +```js title="loader.js" +module.exports = function loader(source) { + console.log(this.rootContext); // /path/to/project + return source; +}; +``` ## this.sourceMap +- **类型:** `boolean` + 是否应该生成一个 source map。 ## this.getLogger() +- **类型:** + ```ts function getLogger(name?: string): void; ```