Использование пользовательского загрузчика веб-пакетов для добавления дополнительных полей в экспорт по умолчанию - PullRequest
0 голосов
/ 11 февраля 2019

Я пытаюсь написать собственный загрузчик веб-пакетов, который загружает демонстрационные файлы 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 Ответ

0 голосов
/ 12 февраля 2019

Выяснили, как это сделать с помощью веб-загрузчика «качки» :

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(), ноэто прекрасно работает!Я планирую опубликовать в качестве модуля позднее на этой неделе (с некоторыми другими функциями).

...