로더는 함수를 내보내는 자바스크립트 모듈입니다. 로드 러너는 이 함수를 호출하여 이전 로더 혹은 리소스 파일의 결과를 전달합니다. 함수의 this
컨텍스트는 webpack과 로드 러너에 의해 채워지며, 로더가 호출스타일을 비동기로 변경하거나 쿼리 매개변수를 가져올 수 있도록 하는 몇가지 유용한 메소드를 제공합니다.
첫 번째 로더에는 리소스 파일의 내용이라는 하나의 인수를 넘깁니다. 컴파일러는 마지막 로더의 결과를 기대합니다. 결과는 모듈의 자바스크립트 소스 코드를 나타내는 String
또는 Buffer
(문자열로 변환됨)여야 합니다. 선택적 소스맵 결과(JSON 객체로)도 전달할 수 있습니다.
단일 결과는 동기 모드에서 반환될 수 있습니다. 여러 결과의 경우 this.callback()
을 호출해야 합니다. 비동기 모드에서는 로드 러너가 비동기 결과를 기다려야 함을 나타내기 위해 this.async()
를 호출해야 합니다. 이는 this.callback()
을 반환합니다. 그 후 로더는 undefined
를 반환하고 해당 콜백을 호출해야 합니다.
/**
*
* @param {string|Buffer} [content] 리소스 파일의 내용
* @param {object} [map] https://github.com/mozilla/source-map 에서 사용할 수 있는 소스맵 데이터
* @param {any} [meta] 메타 데이터, 무엇이든 될 수 있습니다
*/
function webpackLoader(content, map, meta) {
// webpack 로더의 코드
}
다음 섹션에서는 다양한 유형의 로더에 대한 몇 가지 기본 예를 제공합니다. map
및 meta
매개변수는 선택 사항입니다. 아래의 this.callback
을 참고하세요.
return
또는 this.callback
을 사용하여 변환된 content
를 동기적으로 반환할 수 있습니다.
sync-loader.js
module.exports = function (content, map, meta) {
return someSyncOperation(content);
};
this.callback
메소드는 content
만 사용하는 것이 아니라 여러 인수를 전달할 수 있으므로 더 유연합니다.
sync-loader-with-multiple-results.js
module.exports = function (content, map, meta) {
this.callback(null, someSyncOperation(content), map, meta);
return; // callback() 함수를 호출하면 항상 undefined를 반환합니다.
};
비동기 로더의 경우 this.async
는 callback
함수를 가져오기 위해 사용됩니다.
async-loader.js
module.exports = function (content, map, meta) {
var callback = this.async();
someAsyncOperation(content, function (err, result) {
if (err) return callback(err);
callback(null, result, map, meta);
});
};
async-loader-with-multiple-results.js
module.exports = function (content, map, meta) {
var callback = this.async();
someAsyncOperation(content, function (err, result, sourceMaps, meta) {
if (err) return callback(err);
callback(null, result, sourceMaps, meta);
});
};
기본적으로 리소스 파일은 UTF-8 문자열로 변환되어 로더에 전달됩니다. raw
플래그를 true
로 설정하면 로더가 원시 Buffer
를 받게 됩니다. 모든 로더는 결과를 String
혹은 Buffer
로 전달할 수 있습니다. 컴파일러는 로더 사이에서 변환되어 작동됩니다.
raw-loader.js
module.exports = function (content) {
assert(content instanceof Buffer);
return someSyncOperation(content);
// 반환 값도 `Buffer`가 될 수 있습니다.
// 로더가 "raw"가 아닌 경우에도 허용됩니다.
};
module.exports.raw = true;
로더는 항상 오른쪽에서 왼쪽으로 호출됩니다. 로더가 요청 뒤의 메타 데이터에만 관심을 두고 이전 로더의 결과를 무시할 수 있는 경우가 있습니다. 로더의 pitch
메소드는 로더가 실제로 실행(오른쪽에서 왼쪽으로)되기 전에 왼쪽에서 오른쪽으로 호출됩니다.
다음과 같은 use
설정을 살펴보겠습니다.
module.exports = {
//...
module: {
rules: [
{
//...
use: ['a-loader', 'b-loader', 'c-loader'],
},
],
},
};
다음 단계들이 발생합니다.
|- a-loader `pitch`
|- b-loader `pitch`
|- c-loader `pitch`
|- 요청된 모듈이 의존성으로 선택됩니다
|- c-loader 정상 실행
|- b-loader 정상 실행
|- a-loader 정상 실행
그렇다면 로더가 "pitching" 단계를 이용하는 이유는 무엇일까요?
첫째, pitch
메소드에 전달된 data
는 실행 단계에서도 this.data
아래에 노출되며 실행 주기의 초기부터 정보를 캡처하고 공유하는 데 유용할 수 있습니다.
module.exports = function (content) {
return someSyncOperation(content, this.data.value);
};
module.exports.pitch = function (remainingRequest, precedingRequest, data) {
data.value = 42;
};
둘째, 로더가 pitch
방식으로 결과를 전달하면 프로세스가 돌아가 나머지 로더를 건너뜁니다. 아래는 위의 예시에서 b-loader
의 pitch
메소드가 무언가를 반환했을 때의 경우를 살펴봅니다.
module.exports = function (content) {
return someSyncOperation(content);
};
module.exports.pitch = function (remainingRequest, precedingRequest, data) {
if (someCondition()) {
return (
'module.exports = require(' +
JSON.stringify('-!' + remainingRequest) +
');'
);
}
};
위의 단계는 다음과 같이 단축됩니다.
|- a-loader `pitch`
|- b-loader는 `pitch`를 통해 모듈을 리턴합니다
|- a-loader 정상 실행
로더 컨텍스트는 this
속성에 할당된 로더 내부에서 사용할 수 있는 속성을 나타냅니다.
다음 예제가 주어지면 require 호출이 사용됩니다.
/abc/file.js
의 경우
require('./loader1?xyz!loader2!./resource?rrr');
addContextDependency(directory: string)
로더 결과의 의존성으로 디렉터리를 추가합니다.
addDependency(file: string)
dependency(file: string) // 단축
기존 파일을 감시할 수 있게 만들기 위해 로더 결과의 의존성으로 파일을 추가합니다. 예를 들어, sass-loader
, less-loader
는 가져온 css
파일이 변경될 때마다 이를 사용하여 재컴파일합니다.
addMissingDependency(file: string)
존재하지 않는 파일을 감시할 수 있게 만들기 위해 로더 결과의 의존성으로 파일을 추가합니다. addDependency
와 유사하지만 감시자가 올바르게 연결되기 전에 컴파일하는 동안 파일 생성을 처리합니다.
로더가 비동기로 콜백할 예정임을 loader-runner에 알립니다. this.callback
을 반환합니다.
아래는 캐시 가능 여부를 플래그로 설정하는 함수입니다.
cacheable(flag = true: boolean)
기본적으로 로더 결과는 캐시 가능한 것으로 플래그가 지정됩니다. 로더의 결과를 캐시할 수 없도록 하려면 false
를 전달해 이 메소드를 호출하세요.
캐시 가능한 로더는 입력과 의존성이 변경되지 않는 한 변하지 않는 결과를 가져야 합니다. 이는 로더가 this.addDependency
로 지정된 것 이외의 의존성을 갖지 않아야 함을 의미합니다.
여러 결과를 반환하기 위해 동기식 또는 비동기식으로 호출할 수 있는 함수입니다. 예상되는 인수는 다음과 같습니다.
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap?: SourceMap,
meta?: any
);
Error
또는 null
이어야 합니다.string
또는 Buffer
입니다.이 함수가 호출되는 경우 로더의 모호한 결과를 피하고자 undefined를 반환해야 합니다.
clearDependencies();
로더 결과의 모든 의존성을 제거합니다. 이는 초기 의존성과 다른 로더의 의존성을 포함합니다. pitch
사용을 고려하세요.
모듈의 디렉터리. 다른 것을 해석하기 위한 컨텍스트로 사용할 수 있습니다.
이 예제에서 resource.js
가 이 디렉터리에 있기 때문에 /abc
가 됩니다.
로더의 pitch와 정상 실행 단계 간에 공유되는 데이터 객체입니다.
emitError(error: Error)
출력에도 표시될 수 있는 오류를 내보냅니다.
ERROR in ./src/lib.js (./src/loader.js!./src/lib.js)
Module Error (from ./src/loader.js):
Here is an Error!
@ ./src/index.js 1:0-25
emitFile(name: string, content: Buffer|string, sourceMap: {...})
파일을 내보냅니다. 이는 webpack에 따라 다릅니다.
emitWarning(warning: Error)
다음과 같이 출력에 표시될 경고를 내보냅니다.
WARNING in ./src/lib.js (./src/loader.js!./src/lib.js)
Module Warning (from ./src/loader.js):
Here is a Warning!
@ ./src/index.js 1:0-25
생성된 런타임 코드에서 어떤 종류의 ES 기능을 사용할 수 있는지 확인하세요.
예시:
{
// 화살표 함수('() => { ... }')를 지원합니다.
"arrowFunction": true,
// BigInt를 문자 그대로 지원합니다(예를 들어, 123n).
"bigIntLiteral": false,
// const 및 let 변수 선언을 지원합니다.
"const": true,
// destructuring을 지원합니다('{ a, b } = obj' 처럼 사용합니다).
"destructuring": true,
// EcmaScript 모듈을 가져오는 비동기 import() 함수를 지원합니다.
"dynamicImport": false,
// 현재 웹 대상에 대해서만 작업자를 만들 때 async import()를 지원합니다.
"dynamicImportInWorker": false,
// 'for of' 반복자를 지원합니다('for (const x of array) { ... }' 처럼 사용합니다).
"forOf": true,
// 'globalThis'를 지원합니다.
"globalThis": true,
// ECMAScript 모듈을 가져오는 ECMAScript Module syntax를 지원합니다(import ... from '...' 처럼 사용합니다).
"module": false,
// 옵셔널 체이닝('obj?.a' 또는 'obj?.()')을 지원합니다.
"optionalChaining": true,
// 템플릿 리터럴을 지원합니다.
"templateLiteral": true
}
compilation
의 inputFileSystem
속성에 접근합니다.
주어진 로더 옵션을 추출합니다. 선택적으로 JSON 스키마를 인수로 허용합니다.
getResolve(options: ResolveOptions): resolve
resolve(context: string, request: string, callback: function(err, result: string))
resolve(context: string, request: string): Promise<string>
this.resolve
와 유사한 resolve 함수를 만듭니다.
Webpack resolve
옵션 아래의 모든 옵션이 가능합니다. 설정된 resolve
옵션과 병합됩니다. "..."
는 resolve
옵션에서 값을 확장하기 위해 배열에서 사용할 수 있습니다(예: { extensions: [".sass", "..."] }
).
options.dependencyType
은 추가 옵션입니다. 이는 우리가 resolve
옵션에서 byDependency
를 해석하는 데 사용되는 의존성 타입을 지정할 수 있도록 합니다.
해석 작업의 모든 의존성은 현재 모듈에 대한 의존성으로 자동 추가됩니다.
로더용 HMR에 대한 정보입니다.
module.exports = function (source) {
console.log(this.hot); // --hot 플래그 또는 webpack 설정을 통해 HMR이 활성화된 경우 true입니다.
return source;
};
this.importModule(request, options, [callback]): Promise
자식 컴파일러가 빌드 시 요청을 컴파일하고 실행하기 위한 대체 경량 솔루션입니다.
request
: 모듈을 로드할 요청 문자열입니다options
:
layer
: 이 모듈이 배치/컴파일되는 레이어를 지정합니다publicPath
: 빌드된 모듈에 사용되는 공개 경로입니다callback
: 모듈의 export 또는 ESM용 네임스페이스 객체를 리턴하는 Node.js 스타일의 선택적 콜백 함수입니다. 콜백을 제공하지 않으면 importModule
이 Promise를 리턴합니다.webpack.config.js
module.exports = {
module: {
rules: [
{
test: /stylesheet\.js$/i,
use: ['./a-pitching-loader.js'],
type: 'asset/source', // 로더가 문자열을 리턴하므로 타입을 'asset/source'로 설정합니다
},
],
},
};
a-pitching-loader.js
exports.pitch = async function (remaining) {
const result = await this.importModule(
this.resourcePath + '.webpack[javascript/auto]' + '!=!' + remaining
);
return result.default || result;
};
src/stylesheet.js
import { green, red } from './colors.js';
export default `body { background: ${red}; color: ${green}; }`;
src/colors.js
export const red = '#f00';
export const green = '#0f0';
src/index.js
import stylesheet from './stylesheet.js';
// 스타일 시트는 빌드시 문자열 `body { background: #f00; color: #0f0; }`가 됩니다
위 예에서 다음과 같은 사실을 알 수 있습니다
!=!
구문을 사용하여 요청에 대한 matchResource를 설정합니다. 즉, 원본 리소스 대신 module.rules
와 일치시키기 위해 this.resourcePath + '.webpack[javascript/auto]'
를 사용합니다..webpack[javascript/auto]
는 .webpack[type]
패턴의 유사 확장이며, 다른 모듈 타입이 지정되지 않은 경우 기본 모듈 타입을 지정하는 데 사용합니다. 일반적으로 !=!
구문과 함께 사용됩니다.위의 예는 단순화된 예제이므로 webpack 저장소에서 전체 예제를 확인할 수 있습니다.
현재 로더의 로더 배열에 있는 인덱스입니다.
예제에서 loader1은 0
, loader2은 1
입니다.
loadModule(request: string, callback: function(err, source, sourceMap, module))
모듈에 대한 주어진 요청을 해석하고 설정된 모든 로더를 적용하고 생성된 소스, 소스맵 및 모듈 인스턴스(일반적으로 NormalModule
의 인스턴스)로 콜백합니다. 결과를 생성하기 위해 다른 모듈의 소스 코드를 알아야 하는 경우 이 기능을 사용합니다.
로더 컨텍스트의 this.loadModule
은 기본적으로 CommonJS 해석 규칙을 사용합니다. 'esm'
, 'commonjs'
또는 사용자 정의와 같은 다른 해석을 사용하기 전에 적절한 dependencyType
과 함께 this.getResolve
를 사용하세요.
모든 로더의 배열입니다. pitch 단계에서 쓸 수 있습니다.
loaders = [{request: string, path: string, query: string, module: function}]
예제의 내용입니다.
[
{
request: '/abc/loader1.js?xyz',
path: '/abc/loader1.js',
query: '?xyz',
module: [Function],
},
{
request: '/abc/node_modules/loader2/index.js',
path: '/abc/node_modules/loader2/index.js',
query: '',
module: [Function],
},
];
어떤 mode
webpack이 실행되고 있는지 읽습니다.
가능한 값은 'production'
, 'development'
, 'none'
입니다.
options
객체로 설정된 경우 해당 객체를 가리킵니다.options
이 없지만 쿼리 문자열로 호출된 경우 ?
로 시작하는 문자열이 됩니다.해석된 요청 문자열입니다.
예제의 '/abc/loader1.js?xyz!/abc/node_modules/loader2/index.js!/abc/resource.js?rrr'
resolve(context: string, request: string, callback: function(err, result: string))
require 표현식과 같은 요청을 resolve(해석)합니다.
context
는 디렉터리의 절대 경로여야 합니다. 이 디렉터리는 해석을 위한 시작 위치로 사용됩니다.request
는 해석될 요청입니다. 일반적으로 ./relative
와 같은 상대 요청이나 module/path
와 같은 모듈이 요청되지만 /some/path
와 같은 절대 경로도 요청으로 가능합니다.callback
은 resolve된 경로를 제공하는 일반 Node.js 스타일 콜백 함수입니다.resolve 작업의 모든 의존성은 현재 모듈에 대한 의존성으로 자동 추가됩니다.
쿼리를 포함한 요청의 리소스 부분입니다.
예제의 '/abc/resource.js?rrr'
리소스 파일입니다.
예제의 '/abc/resource.js'
리소스의 쿼리입니다.
예제의 '?rrr'
Webpack 4부터 기존의 this.options.context
는 this.rootContext
로 제공됩니다.
소스맵을 생성해야 하는지 여부를 알려줍니다. 소스맵을 생성하는 것은 비용이 많이 드는 작업이므로 소스맵이 실제로 요청되었는지 확인해야 합니다.
컴파일 대상입니다. 설정 옵션에서 전달됩니다.
예: 'web'
, 'node'
다음 유틸리티에 접근합니다.
absolutify
: 가능한 경우 절대 경로를 사용하여 새 요청 문자열을 리턴합니다.contextify
: 가능한 경우 절대 경로를 피하는 새 요청 문자열을 리턴합니다.createHash
: 제공된 해시 함수에서 새 Hash 객체를 반환합니다.my-sync-loader.js
module.exports = function (content) {
this.utils.contextify(
this.context,
this.utils.absolutify(this.context, './index.js')
);
this.utils.absolutify(this.context, this.resourcePath);
const mainHash = this.utils.createHash(
this._compilation.outputOptions.hashFunction
);
mainHash.update(content);
mainHash.digest('hex');
// …
return content;
};
로더 API 버전입니다. 현재는 2
버전입니다. 이는 이전 버전과의 호환성을 제공하는 데 유용합니다. 버전을 사용하여 커스텀 동작 또는 주요 변경 사항에 대한 대체를 지정할 수 있습니다.
이 boolean은 webpack에 의해 컴파일될 때 true로 설정됩니다.
로더 인터페이스는 모든 모듈 관련 정보를 제공합니다. 그러나 드문 경우로 컴파일러 API 자체에 접근해야 할 수도 있습니다.
그러므로 최후의 수단으로만 사용해야 합니다. 이 속성들을 사용하면 로더의 범용성이 떨어집니다.
Webpack의 현재 Compilation 객체에 접근합니다.
Webpack의 현재 Compiler 객체에 접근합니다.
Boolean 플래그입니다. 디버그 모드일 때 설정됩니다.
마지막 로더에서 전달되었습니다. 입력 인수를 모듈로 실행하려는 경우 이 변수를 바로가기(성능용)로 읽는 것을 고려하세요.
결과가 최소화 되어야 하는지 여부를 알려줍니다.
다음 로더에 값을 전달합니다. 모듈로 실행했을 때 결과가 무엇을 내보내는지 알고 있다면 여기에서 이 값을 설정(유일한 배열 요소로)하십시오.
로드되는 모듈 객체에 대한 hacky 접근.
다음을 통해 로더 내부에서 오류를 보고할 수 있습니다.
throw
(혹은 기타 잡히지 않는 예외)를 사용합니다. 로더가 실행되는 동안 오류가 발생하면 현재 모듈 컴파일이 실패합니다.callback
(비동기 모드에서)를 사용합니다. 콜백에 오류를 전달하면 모듈 컴파일 실패도 발생합니다.예시:
./src/index.js
require('./loader!./lib');
로더에서 오류가 발생하는 경우입니다.
./src/loader.js
module.exports = function (source) {
throw new Error('This is a Fatal Error!');
};
또는 비동기 모드에서 콜백에 오류를 전달하는 경우입니다.
./src/loader.js
module.exports = function (source) {
const callback = this.async();
//...
callback(new Error('This is a Fatal Error!'), source);
};
모듈은 다음과 같이 번들로 제공됩니다.
/***/ "./src/loader.js!./src/lib.js":
/*!************************************!*\
!*** ./src/loader.js!./src/lib.js ***!
\************************************/
/*! no static exports found */
/***/ (function(module, exports) {
throw new Error("Module build failed (from ./src/loader.js):\nError: This is a Fatal Error!\n at Object.module.exports (/workspace/src/loader.js:3:9)");
/***/ })
그후 다음 빌드 출력에도 오류가 표시됩니다(this.emitError
와 유사합니다).
ERROR in ./src/lib.js (./src/loader.js!./src/lib.js)
Module build failed (from ./src/loader.js):
Error: This is a Fatal Error!
at Object.module.exports (/workspace/src/loader.js:2:9)
@ ./src/index.js 1:0-25
아래에서 볼 수 있듯이 오류 메시지뿐만 아니라 관련 로더 및 모듈에 대한 세부 정보도 표시됩니다.
ERROR in ./src/lib.js
(./src/loader.js!./src/lib.js)
(from ./src/loader.js)
@ ./src/index.js 1:0-25
새로운 인라인 요청 구문이 webpack v4에 도입되었습니다. 요청에 접두사 <match-resource>!=!
를 붙이면 이 요청에 대한 matchResource
가 설정됩니다.
matchResource
가 설정되면 원래 리소스 대신 module.rules
와 일치하는 데 사용됩니다. 이는 리소스에 추가 로더를 적용해야 하거나 모듈 유형을 변경해야 하는 경우에 유용할 수 있습니다. 또한 통계에 표시되며 splitChunks
에서 test
와 Rule.issuer
를 일치시키는 데 사용됩니다.
예제:
file.js
/* STYLE: body { background: red; } */
console.log('yep');
로더는 파일을 다음 파일로 변환하고 matchResource
를 사용하여 사용자 지정 CSS 처리 규칙을 적용할 수 있습니다.
file.js (로더에 의해 변환되었습니다)
import './file.js.css!=!extract-style-loader/getStyles!./file.js';
console.log('yep');
이는 extract-style-loader/getStyles!./file.js
에 종속성을 추가하고 결과를 file.js.css
로 처리합니다. module.rules
에는 /\.css$/
와 일치하는 규칙이 있고 이 종속성에 적용되기 때문입니다.
로더는 다음과 같이 보일 수 있습니다.
extract-style-loader/index.js
const getStylesLoader = require.resolve('./getStyles');
module.exports = function (source) {
if (STYLES_REGEXP.test(source)) {
source = source.replace(STYLES_REGEXP, '');
return `import ${JSON.stringify(
this.utils.contextify(
this.context || this.rootContext,
`${this.resource}.css!=!${getStylesLoader}!${this.remainingRequest}`
)
)};${source}`;
}
return source;
};
extract-style-loader/getStyles.js
module.exports = function (source) {
const match = source.match(STYLES_REGEXP);
return match[0];
};
로깅 API는 webpack 4.37 릴리즈부터 사용할 수 있습니다. stats 설정
에서 logging
이 활성화되거나 infrastructure 로깅
이 활성화되면 로더가 메세지를 기록할 수 있으며 이 메세지는 해당 로거 형식(stats, infrastructure)으로 출력됩니다.
this.getLogger()
를 사용하는 것을 선호해야 합니다. 이는 로더 경로와 처리된 파일을 포함한 compilation.getLogger()
의 단축어입니다. 이러한 종류의 로깅은 stats에 저장되고 그에 따라 형식이 지정됩니다. webpack 사용자가 필터링하고 내보낼 수 있습니다.this.getLogger('name')
를 사용하여 자식 이름을 가진 독립 로거를 얻을 수 있습니다. 로더 경로 및 처리된 파일은 계속 추가됩니다.getLogger
방식을 지원하지 않는 이전 webpack 버전이 사용되는 경우 대체 코드를 제공하기 위해 this.getLogger ? this.getLogger() : console
를 감지하는 특정한 대체 코드를 사용할 수 있습니다.