Настройте способ работы «импорта» с помощью webpack

Мне нужно настроить способ обработки импорта в моем приложении веб-пакетом.

Некоторые из моих сервисов имеют фиктивные реализации. В тестовом режиме я хочу импортировать фиктивный файл вместо реального сервиса, если рядом с сервисом существует файл с постфиксом «.mock», в противном случае импортирую сам сервис.

Обратите внимание, что мне нужны разные файлы вывода (main.js и test.js). Поэтому мне нужно убедиться, что test.js не включает реальные реализации сервисов (недостаточно предотвращения выполнения, исходный код не следует импортировать вообще).

Папка служб содержит следующие файлы:

service-1.js
service-1.mock.js
service-2.js
index.js

services / index.js:

import service1 from ‘./service-1’
import service2 from ‘./service-2’
export {service1, service2}

Посоветуйте, пожалуйста, как мне настроить свой веб-пакет.

Какую среду тестирования вы используете? Jasmine, Mocha, Jest и т. д. - у большинства из них есть способы справиться с подобными проблемами. Я не думаю, что изменение webpack здесь - правильный подход.

Patrick Hund 08.09.2018 17:15

@PatrickHund, чтобы убедиться, что все посетители могут понять мою ситуацию, я объяснил свою проблему более простым способом, однако, похоже, я не был таким успешным, лол. На самом деле это не связано с запуском тестов. Я хочу иметь разные файлы вывода для настольных и мобильных устройств с .desktop и .mobile в качестве дополнительных постфиксов для моих исходных файлов.

Saeed Seyfi 08.09.2018 17:27
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
2
122
1

Ответы 1

Согласно комментариям, я могу предложить эти обходные пути с помощью пользовательского загрузчика:

Способ # 1

Создайте по одному .mobile и .desktop для каждого из ваших компонентов в основном файле (например, component.js, component.mobile.js, component.desktop.js) и используйте этот пользовательский загрузчик:

const targets = {
  desktop: 'desktop',
  mobile: 'mobile'
};
const source = `
    import Home from './components/home';
    import About from './components/about';
    import Header from './shared/Header';
    import Footer from './shared/about';
    import Categories from './category/categories';
    
    // rest of code
`;

const manipulated = manipulateSource(source, targets.mobile, ['components', 'shared']);
console.info(manipulated);


function manipulateSource(src, target = targets.desktop, pathMatches = []) {
  const paths = pathMatches.length ? `(${pathMatches.join('|')})` : '';
  const pattern = new RegExp(`(?<left>.*import.*${paths}.*\\/)(?<name>[\\w\\-_]*)(?<rest>.*\\n)`, 'g');
  const manipulated = src.replace(pattern, (...args) => {
    const [{
      left,
      name,
      rest
    }] = args.slice(-1);
    return `${left}${name}.${target}${rest}`;
  });

  return manipulated;
}

Способ # 2

Поскольку эти файлы имеют разные реализации для .mobile и .desktop, создайте третий файл (или четвертый, если вы хотите поместить общий код в основной файл) с тем же именем и значимым расширением (например: component.platformAdaptive.js), который можно обрабатывать с помощью regular expresion (или любого другого другой способ манипуляции). В этом методе вам может потребоваться поместить базовую реализацию в последний файл, если вы используете strongTypes (например, Typescript):

const targets = {
  desktop: 'desktop',
  mobile: 'mobile'
};
const source = `
    import Home from './components/home';
    import About from './components/about';
    import Header from './shared/Header.platformAdaptive';
    import Footer from './shared/about.platformAdaptive';
    import Categories from './category/categories.platformAdaptive';
    
    // rest of code
`;

const manipulatedMob = manipulateSource(source, 'platformAdaptive', targets.mobile);
const manipulatedDesk = manipulateSource(source, 'platformAdaptive');

console.info(manipulatedMob);
console.info(manipulatedDesk);

function manipulateSource(src, replace, target = targets.desktop) {
  const pattern = new RegExp(`(?<left>.*\\/)(?<name>[\\w\\-_]*\.)${replace}(?<rest>.*\\n)`, 'g');
  const manipulated = src.replace(pattern, (...args) => {
    const [{
      left,
      name,
      rest
    }] = args.slice(-1);
    return `${left}${name}${target}${rest}`;
  });

  return manipulated;
}

Оба вышеуказанных метода имеют некоторые ограничения при импорте, например, вы не можете использовать Barrel files (index.js), поскольку они предполагают, что последний фрагмент импорта является файлом компонента. В этом случае вы можете добавить несколько папок с barrel для обработки этого импорта. например, во втором методе вам понадобится такая структура:

|-- components.platformAdaptive
    |-- index.js
|-- components.mobile
    |-- index.js
|-- components.desktop
    |-- index.js

Или вы можете использовать / вместо . для создания вложенной структуры (например, components/platformAdaptive):

|-- components
    |-- [+] platformAdaptive
    |-- [+] mobile
    |-- [+] desktop

Способ # 3

Другой способ справиться с этой ситуацией - иметь разные классы с разными именами. Например, компонент List с различными реализациями для мобильных устройств и настольных ПК, тогда будет три компонента, таких как ListPlatformAdaptive, ListMobile, ListDesktop, в которых ListPlatformAdaptive может иметь базовые реализации, и barrel в папке компонентов, которая экспортирует компоненты:

import * as ListPlatformAdaptive from './list.platformAdaptive';
import * as ListMobile from './list.mobile';
import * as ListDesktop from './list.desktop';

export {
    ListPlatformAdaptive,
    ListMobile,
    ListDesktop
}

Структура будет такой:

|-- components
    |-- list.platformAdaptive.js
    |-- list.mobile.js
    |-- list.desktop.js
    |-- index.js

Тогда манипуляция будет такой:

const targets = {
  desktop: 'Desktop',
  mobile: 'Mobile'
};
const source = `
    import Home from './components/home';
    import About from './components/about';
    import HeaderPlatformAdaptive as Header from './shared/Header';
    import FooterPlatformAdaptive as Footer from './shared/about';
    import CategoriesPlatformAdaptive as Categories from './category/categories';
    
    // rest of code
`;

const replace = 'PlatformAdaptive';
const manipulatedMob = manipulateSource(source, replace, targets.mobile);
const manipulatedDesk = manipulateSource(source, replace);

console.info(manipulatedMob);
console.info(manipulatedDesk);

function manipulateSource(src, replace, target = targets.desktop) {
  const pattern = new RegExp(replace, 'g');
  const manipulated = src.replace(pattern, target);

  return manipulated;
}

При использовании этого метода вы должны быть осторожны с файлами barrel, которые должны быть исключены, и обратная сторона этого метода заключается в том, что все компоненты уже импортированы, поэтому стоимость импорта неприемлема.


Метод # 4

Другой способ, который я могу придумать, - это добавить несколько примечаний в качестве комментария и отреагировать на его существование в этой строке:

const targets = {
  desktop: 'Desktop',
  mobile: 'Mobile'
};
const source = `
    import Home from './components/home';
    import About from './components/about';
    import Header from './shared/Header'; /* @adaptive */
    import Footer from './shared/about';  /* @adaptive: Desktop */
    import Categories from './category/categories'; /* @adaptive: Mobile */
                
    // rest of code
`;

const manipulatedMob = manipulateSource(source, targets.mobile);
const manipulatedDesk = manipulateSource(source);

console.info(manipulatedMob);
console.info(manipulatedDesk);

function manipulateSource(src, targetDevice = targets.desktop) {
  const pattern = /(?<left>.*import\s+)(?<name>\w+)(?<rest1>.*)\@adaptive(\:\s*(?<target>\w+))?(?<rest2>.*)/g
  const manipulated = src.replace(pattern, (matched, ...args) => {
    let [{
      left,
      name,
      rest1,
      target,
      rest2
    }] = args.slice(-1);
    target = target || targetDevice;
    return target == targetDevice ?
      `${left}${name}${target}$ as ${name}${rest1}${rest2}` :
      matched;
  });

  return manipulated;
}

В этом методе, таком как метод № 2, имена импортированных компонентов отличаются от оригинальных, но сопоставлены с оригинальным именем, что совсем не подходит, но мне это нравится больше всего, поскольку при использовании его в файлах barrel можно изменить адрес импортированного файла. Еще одна интересная часть может заключаться в передаче адреса целевых файлов относительно target device и его синтаксическом анализе.

Заключение

Как видите, все мои ответы касались источников дескрипторов без проверки существования файла и предположения, что разработчик в этом уверен. Кстати, вы можете выполнить поиск, чтобы узнать, есть ли способ найти абсолютный путь к файлу, а затем проверить наличие целевых заменителей.

Другие вопросы по теме