Estou muito impressionado com os namespaces em linguagens de programação como Java e PHP. Tanto que até de alguma forma escrevi um artigo sobre eles no Habré. Quase dois anos se passaram desde então, mas os namespaces não apareceram em JavaScript durante esse tempo. “ E se eu fizesse namespaces em JS para mim, o que seriam? ” - pensei. Sob o corte - meus pensamentos, quais namespaces eu preciso em JavaScript.
Introdutório
Todo o meu raciocínio abaixo se aplica aos módulos ES6 e não toca em outros formatos (AMD, UMD, CommonJS) simplesmente porque estou interessado em ver para onde o JavaScript está indo, não para onde estava . Além disso, na minha prática, de alguma forma encontrei o GWT bastante próximo, após o que desenvolvi uma rejeição persistente de vários transpilers (bem como, para um heap, minificadores e ofuscadores). Portanto, JS vanilla e não TS. Bem, eu tenho esses itens.
Módulos ES6
Um módulo ES é um arquivo de origem separado que define explicitamente os elementos disponíveis fora do módulo:
export function fn() {/*...*/}Portanto, para começar, você precisa abordar de alguma forma os módulos ES individuais em todo o aplicativo.
Pacotes
, . . (vendor) , . (, ./src).
node_modules. , nodejs-, , :
* node_modules
* @vendor
* package1
* src
* module1.js
* ...
* moduleN.js
* ...
* packageN
* src
* module1.mjs
* ...
* moduleN.mjsES- :
./node_modules/@vendor/package1/src/module1.js
...
./node_modules/@vendor/packageN/src/moduleN.mjs nodejs- ./node_modules/ :
import SomeThing from '@vendor/package1/src/module1.js';, , :
import SomeThing from './module1.js'; web- , web- node_modules, web- ES-, , nodejs:
<script type="module">
import {fn} from './@vendor/package1/src/module1.js'
fn();
</script>:
<script>
import('./@vendor/package1/src/module1.js').then((mod) => {
mod.fn();
});
</script> , web' ./ . :
import {fn} from '@vendor/package1/src/module1.js':
Uncaught TypeError: Failed to resolve module specifier "@vendor/package1/src/module1.js". Relative references must start with either "/", "./", or "../".
, ES-:
( ):
./module1.js(nodejs):
@vendor/package1/src/module1.js(web):
./@vendor/package1/src/module1.js
./ nodejs-, ./ .
, JS- , , ( - ) , ( ).
" " ( , namespace'), ES- ( ), ES- , , nodejs, .
, ./, , ( , ):
@vendor/package1/src/module1 - : ./src/, ./lib/, ./dist/. - , , :
@vendor/package1/module1, , .
Namespace mapping
, , . - web-, node_modules web- ( - ./packages/):
const node = {
'@vendor/package1': {path: '/.../node_modules/@vendor/package1/src', ext: 'js'},
'@vendor/packageN': {path: '/.../node_modules/@vendor/packageN/src', ext: 'mjs'},
};
const browser = {
'@vendor/package1': {path: 'https://.../packages/@vendor/package1/src', ext: 'js'},
'@vendor/packageN': {path: 'https://.../packages/@vendor/packageN/src', ext: 'mjs'},
};Module loader
, '' ( @vendor/package1/module1) ( - ) (node ):
@vendor/package1/module1 => /.../node_modules/@vendor/package1/src/module1.js // node
@vendor/packageN/moduleN => https://.../packages/@vendor/packageN/src/moduleN.mjs // browsere usá-lo para importar módulos dinamicamente. Obviamente, não há necessidade de mapear todos os módulos do pacote - você só precisa mapear a raiz do pacote. A saída é mais ou menos assim:
const loader = new ModuleLoader();
loader.addNamespace('@vendor/package1', {path: '/.../node_modules/@vendor/package1/src', ext: 'js'});
// ...
loader.addNamespace('@vendor/packageN', {path: '/.../node_modules/@vendor/packageN/src', ext: 'js'});
const module1 = await loader.import('@vendor/package1/module1');A importação de módulos deve ser assíncrona, uma vez que uma função assíncrona será usada internamente import().
Resumo
De forma tão elegante, seria possível alternar do endereçamento físico de módulos ES durante a importação para seu endereçamento lógico (namespaces) e usar os mesmos módulos para aplicativos nodejs e no navegador. Nada de novo foi inventado aqui ( algo semelhante já foi feito no PHP, de onde essa ideia foi roubada).