Использование пользовательского загрузчика веб-пакетов для добавления дополнительных полей в экспорт по умолчанию
Я пытаюсь написать собственный загрузчик веб-пакетов, который загружает демонстрационные файлы javascript вместе с их исходным кодом для целей документирования. Должно
- Загрузите указанный файл JavaScript (например,
Simple.demo.js
) - Прикрепите необработанный источник файла к экспорту по умолчанию в качестве нового поля
Когда это будет сделано, я смогу настроить загрузчик в своем конфиге веб-пакета:
{
test: /\.demo.js$/,
exclude: /node_modules/,
use: [{
loader: "babel-loader",
}, {
loader: resolve("./demo-loader.js"),
}]
}
… Затем получите доступ к исходному коду для моей демонстрации после импорта:
import SimpleDemo from 'demo-loader!./Simple.demo.js';
console.log(SimpleDemo.__source__);
SimpleDemo(); //run the demo code
Попытка 1
Я успешно загрузил исходный код и выполнил именованный экспорт, используя следующий загрузчик:
const { readFile } = require("fs");
const escapeSource = require("js-string-escape");
module.exports = function withSourceLoader(content, map, meta) {
const onComplete = this.async();
const fileName = this.resource;
readFile(fileName, (error, fileContents) => {
if ( error ) {
return onComplete(error);
}
const exportSource = `export const rawSource = "${ escapeSource(fileContents)}";`;
const newContent = `${ content }\n\n${ exportSource }`;
onComplete(null, newContent, map, meta);
});
};
Это позволяет мне сделать
import SimpleDemo, { rawSource } from 'demo-loader!./Simple.demo.js`;
console.log(rawSource);
SimpleDemo(); //run the demo code
... но я хочу передать все SimpleDemo
как объект для другой функции, поэтому я хотел бы импортировать их как один блок (будет много демонстраций).
Попытка 2
Поскольку я не знаю имя внутренней переменной, используемой для экспорта по умолчанию в демонстрационном файле (без выполнения полного анализа AST), я не могу просто добавить дополнительную строку в файл, чтобы изменить его. Вместо этого я попытался написать файл-обертку, который импортирует и повторно экспортирует файл по умолчанию, например так:
const { readFile } = require("fs");
const { basename } = require("path");
const escapeSource = require("js-string-escape");
module.exports = function withSourceLoader(content, map, meta) {
const onComplete = this.async();
const fileName = this.resource;
readFile(fileName, (error, fileContents) => {
if ( error ) {
return onComplete(error);
}
const newContent = `
const rawSource = "${ escapeSource(fileContents) }";
import DemoDefault from './${ basename(fileName) }';
DemoDefault.__source__ = rawSource;
export default DemoDefault;
`;
onComplete(null, newContent, map, meta);
});
};
Я надеюсь, что это новое import
директива будет решена с помощью веб-пакета (я знаю, что это может привести к бесконечной регрессии, потому что она должна просто снова запустить тот же загрузчик в зависимости от имени файла). Кажется, этого не происходит - я просто получаю
TypeError: _Simple_demo_js__WEBPACK_IMPORTED_MODULE_0__.default is undefined
это происходит потому, что webpack уже построил свое дерево зависимостей и не хочет добавлять к нему что-либо еще. Я также пытался использовать require()
вместо import
это не улучшит дела.
Я поступаю об этом неправильно? Я хотел использовать что-то простое с цепочкой, объединяя выходные данные других загрузчиков, но API, похоже, не может это сделать.
1 ответ
Выяснили, как это сделать, используя веб-пакетный загрузчик:
const { readFile } = require("fs");
const escapeSource = require("js-string-escape");
const { stringifyRequest } = require("loader-utils");
module.exports = function empty() { /* do nothing */ };
module.exports.pitch = function withSourceLoader(remainingRequest) {
const onComplete = this.async();
const filename = this.resource;
readFile(filename, (error, fileContents) => {
if ( error ) {
return onComplete(error);
}
const newContent = `
module.exports = require(${stringifyRequest(this, "!!" + remainingRequest)});
const rawSource = "${ escapeSource(fileContents) }";
module.exports.default.__source__ = rawSource;
`;
onComplete(null, newContent);
});
};
До сих пор не 100% ясно, на магию, как мой require()
вызов разрешается, но работает отлично! Я планирую опубликовать в качестве модуля позже на этой неделе (с некоторыми другими функциями).