Согласно комментариям я могу предложить эти обходные пути, используя пользовательский загрузчик:
Метод № 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.log(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.log(manipulatedMob);
console.log(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.log(manipulatedMob);
console.log(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.log(manipulatedMob);
console.log(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
и проанализировать его.
Заключение
Как вы видите, все мои ответы касались источников дескрипторов без проверки существования файла и предполагают, что разработчик в этом уверен. Кстати, вы можете найти, чтобы найти абсолютный путь к файлу, а затем проверить наличие целевых заменителей.