Carregamento automático do módulo usando importação dinâmica

Recentemente , o suporte aos módulos ECMAScript foi anunciado no Node.js, e o suporte para importações dinâmicas apareceu no ES2020. Na estrutura deste artigo, falarei sobre a implementação do caso óbvio do uso de importações dinâmicas - com nomes de diretório desconhecidos com antecedência.


cobertura


Edição


Frequentemente, observo aproximadamente a seguinte estrutura de diretĂłrios nos projetos:


$ tree . ├── modules │ ├── a │ │ └── index.ts │ ├── b │ │ └── index.ts │ └── c │ └── bobule.ts ├── index.ts └── package.json 

e o conteĂşdo de index.ts :


 import a from './modules/a'; import b from './modules/b'; import c from './modules/c/bobule.ts'; export default { module: a, dopule: b, bobule: c }; 

E, em algum lugar no nível superior, há outro index.ts que importa esse index.ts que importa ...


Gostaria de escrever algo como index.ts nĂ­vel index.ts


 import modules from './modules/*/*' 

mas fora da caixa, isso não é suportado, o que despertou em mim um desejo irreprimível de ferrar muleta, bicicleta, quadro Sem dúvida, uma solução útil, original e muito necessária para esse problema.


Importações dinâmicas


A principal vantagem de importar dinâmico sobre estático é a forma funcional, que permite carregar módulos por condição. Funciona assim:


 // module.ts export const a = 'i love hexlet' const b = { referral: 'hexlet.io/?ref=162475' } export default b // index.ts const module = await import('./module.ts') module.default // { referral: 'hexlet.io/?ref=162475' } module.a // 'i love hexlet' 

Assim, adicionando um pouco de fs a esse design, as importações dinâmicas permitirão que você obtenha todos os arquivos dos subdiretórios de qualquer nível.


Inspirado em PHP


A idéia de carregamento automático não é nova e é ativamente usada em PHP, embora por razões arquitetônicas e históricas, mas nada me impeça de criar minhas próprias dificuldades e superá-las heroicamente. Portanto, tentei criar uma seção de carregamento automático em package.json e criar uma ferramenta que lê o nome do módulo por chave e o caminho do arquivo a partir do valor:


 //  package.json { "autoload": { "modules": ["modules", "*", "index.ts"] "bobules": ["*", "*", "bobule.ts"], } } 

No caso do uso de digitação, há um momento irritante com o fato de as extensões mudarem após a criação do aplicativo e existirem mais de duas delas: ts|js|mjs|tsx para que você possa levar isso imediatamente em consideração, listando todas as opções disponíveis e baixe apenas as que você precisa:


 //  package.json { "autoload": { "modules": ["modules", "*", "index.ts|js"] "bobules": ["*", "*", "bobule.ts|js"], } } 

Implementação


Os seguintes casos sĂŁo obtidos:


  1. f(projectRoot, ['modules', '*', 'index.js|ts'], moduleName = 'default') // carrega os módulos no caminho especificado pelo usuário
  2. f(projectRoot) // carregando os módulos do package.json, os nomes dos módulos (chaves na seção de carregamento automático) nesse caso são passados ​​pelo terceiro argumento já "sob o capô".

Construir caminhos é uma tarefa trivial, basta percorrer a matriz e selecionar todos os subdiretórios para as estrelas, quando a matriz terminar, retorná-la e carregar os módulos na matriz. Como resultado, por algumas noites, desenhei para mim esta solução desta maneira:


 //   package.json /   const modulesRawPathsParts = await getModulesRawPaths( projectRoot, modulePath, moduleGroupName ); //         const modulesFilesPathsParts = entries(modulesRawPathsParts).reduce( (acc, [moduleName, moduleRawPath]) => { const rawFilename = moduleRawPath.pop(); const processedFilenames = processFileExtensions(rawFilename); const pathsWithFilenames = processedFilenames.map( filename => moduleRawPath.concat(filename) ); return { ...acc, [moduleName]: pathsWithFilenames }; }, {} ); //       const modulesFilesPaths = await Promise.all( entries(modulesFilesPathsParts).map(([moduleName, modulePathParts]) => Promise.all( modulePathParts.map(modulePathPart => buildPaths(projectRoot, modulePathPart)) ) .then(paths => paths.flat().filter(processedPath => processedPath)) .then(existingPaths => ({ [moduleName]: existingPaths })), ), ); const processedModulesFilesPaths = arrayToObject(modulesFilesPaths); //    ,       const availableModules = entries(processedModulesFilesPaths).reduce( (acc, [moduleName, modulePaths]) => (modulePaths.length === 0 ? acc : { ...acc, [moduleName]: modulePaths }), {}, ); //   return Promise.all( entries(availableModules).map(([moduleName, modulePaths]) => Promise.all(modulePaths.map(moduleLoadPath => //     : import(moduleLoadPath) )).then(loadedModule => ({ [moduleName]: loadedModule, })), ), ).then(arrayToObject); 

apenas texto datilografado deixado para enganar


Por que isso Ă© tudo?


Pareceu-me que o problema das importações dinâmicas era injustamente muito mal coberto e todas essas bibliotecas no npm não são levemente atualizadas (ou eu estava parecendo mal?), E a tecnologia permite que você faça bem sem registrar e enviar SMS. Espero que o código-fonte do projeto e meus casos de uso o interessem para uso em seus projetos, reduzindo um pouco a duplicação de código estragando um novo muleta, bicicleta, quadro sem dúvida um ajudante útil.




Links, provas, traduções:



O código fonte deste código sem falhas está aqui:
https://github.com/Melodyn/npm-dynamicimport/blob/master/lib/index.js#L93-L120
Obtenha uma experiência valiosa do usuário aqui:
https://www.npmjs.com/package/@melodyn/dynamicimport
O gato está aqui:
(^ ≗ω≗ ^)

Source: https://habr.com/ru/post/pt478974/


All Articles