@EDiPensier

Есть ли способ привязать деструктурированный импорт к свойствам модуля (концепция live bindings) в commonjs без esm?

Имеется условный js-конфигурационный файл, который экспортируется в виде объекта со свойствами.

Некоторые из свойств могут быть установлены по "ленивому" методу, то есть не сразу, а в процессе инициализации, но экспорт всех превентивный.

Пример абстрактного configs.js:

let someLazy = null;

module.exports = { someLazy, someOther: true };

Чтобы повысить читабельную способность, в том числе, уменьшая строки кода, обычно используется деструктурированный импорт. В обычном commonjs поведение деструктуризации таковое, что создаётся локальная переменная без всякой привязки к свойству исходного объекта. Однако же в стандартах import/export результат отличается и эту концепцию называют "live bindings" .

Предположительный setLazy.js:

const configs = require("./configs");

setTimeout(() => {
   configs.someLazy = true;
}, 1000);

Основной допустимый модуль main.js:

//Подключение логики setLazy
require("./setLazy");

//Деструктурированный импорт
const { someLazy } = require("./configs");

//Стандартный импорт
const configs = require("./configs");

setTimeout(() => {

    //Значение здесь будет изменено на true
    console.log(configs.someLazy);

    //А тут останется null, что и надобно изменить, приводя к поведению import/export esm, оставив при этом читабельный импорт commonjs'а
    console.log(someLazy);

}, 1001);

Конечно можно использовать обычную импортную составляющую, но это приведёт к потере удобств, а также, если свойства будут слишком длинными, то таковая потеря случится не только вдвойне, но и способна затронуть производительность в ситуации, когда инициализировано большое количество объектов(интерпретатор лично не сокращает длинные названия, скорее всего из-за развитой рефлексии языка).

Подытоживая, собственно, есть ли какие-нибудь ухищрения, чтобы заставить работать только вышеописанную часть для импортов в commonjs'е по исключительно концепции live-bindings из es-modules?

Или, возможно ли как-нибудь настроить webpack, дабы он обращался исключительно, исходя из вышеперечисленных примеров, к свойству по исходному конфигурационному объекту - configs.someLazy , сохраняя на сборочной стороне деструктуризацию?

P.S. Сами конструкции import/export esm не рассматриваются в качестве альтернативы никак, поскольку имеют существенные недостатки для моего контекста: невозможность переопределить import, так как require через Module.wrap; рефлексия исключительно для динамической составляющей; дефолтные принципы экспорта, в перспективе уменьшающие производительность.
  • Вопрос задан
  • 91 просмотр
Решения вопроса 1
@EDiPensier Автор вопроса
В конце концов пришёл к гибридному решению вопроса - совмещение import'ов для подключения изменяемых свойств и require для противоположных в связке с вебпаком.

Однако такой способ совершенно не подходит, если требуется запустить неупакованное серверное приложение(без учёта специфики сабжа). Соответственно, финальным аккордом стало дописывание костыля, который временно преобразует импорт commonjs'а в esm , если видит на строке выше комментарий //useLiveBindings:

example.js:

//useLiveBindings
const { a, b } = require("./c");
//Преобразуется в import { a, b } from "./c"

//Не будет преобразовано
const { d } = require("./e");

precompileLiveBindings.js:

const nodePath = require("path");
const fs = require("fs");

const regexpToFindLB = /\/\/useLiveBinding(?:\r|\r\n|\n)(?:const|let|var)\s+\{([\s\S]+?)\}\s*=\s*require\s*\(\s*((["'`])[\s\S]+?\3)\s*\)\s*;?/g;

const tempFilePathsStore = {};

const precompileLB = nowDir => {
	
	const filesAndDirs = fs.readdirSync(nowDir);
	
	for(let i = 0; i < filesAndDirs.length; ++i) {
		
		const object = nodePath.join( nowDir, filesAndDirs[i] );
		
		if( fs.lstatSync( object ).isDirectory() ) {
			
			precompileLB(object);
			
		} else {
			
			let content = fs.readFileSync( object ).toString();
			
			const match = !!content.match(regexpToFindLB);
			
			if(match) {
				
				tempFilePathsStore[object] = content;
				
				content = content.replace(regexpToFindLB, `import {$1} from $2`);
				
				fs.writeFileSync( object, content );
				
			}
			
		}
		
	}
	
};

const restoreOriginals = () => {
	
	for(const path in tempFilePathsStore) {
		
		fs.writeFileSync( path, tempFilePathsStore[path] );
		
	}
	
};

module.exports = {
	
	precompileLB: () => precompileLB(__dirname),
	
	restoreOriginals,
	
};

webpack.js:

const webpack = require("webpack");
const { precompileLB, restoreOriginals } = require("./_precompileLB");
const configs = { ... };

const pack = () => {
	
	precompileLB();
	
	webpack(configs, (err, stats) => {
		
		if (!err) err = stats.toJson().errors[0];
		
		if (err) {
			
			throw new Error(err);
			
		}
		
		console.log( stats.toString( { colors: true } ) );
		
		restoreOriginals();
		
	});
	
};

pack();
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы