Como carregar todos os arquivos em um diretório usando webpack sem requerer instruções

95

Eu tenho uma grande quantidade de arquivos javascript divididos em 4 subdiretórios em meu aplicativo. No grunhido, pego todos eles e os compilo em um arquivo. Esses arquivos não têm uma função module.exports.

Quero usar o webpack e dividi-lo em 4 partes. Não quero entrar manualmente e exigir todos os meus arquivos.

Gostaria de criar um plug-in que, na compilação, percorra as árvores de diretório, pegue todos os nomes e caminhos de arquivo .js e, em seguida, exija todos os arquivos nos subdiretórios e os adicione à saída.

Quero que todos os arquivos em cada diretório sejam compilados em um módulo que eu possa solicitar de meu arquivo de ponto de entrada ou incluir nos recursos que http://webpack.github.io/docs/plugins.html menciona.

Ao adicionar um novo arquivo, quero apenas colocá-lo no diretório correto e saber que será incluído.

Existe uma maneira de fazer isso com o webpack ou um plugin que alguém escreveu para fazer isso?

ChickenFur
fonte

Respostas:

119

Isso é o que eu fiz para conseguir isso:

function requireAll(r) { r.keys().forEach(r); }
requireAll(require.context('./modules/', true, /\.js$/));
splintor
fonte
Existe alguma maneira de especificar qual carregador usar? Meu caso de uso é que eu quero usar o image-size-loaderpara todas as imagens a fim de criar espaços reservados com proporções corretas.
bobbaluba
13
você poderia fornecer um exemplo de trabalho webpack.config.js
Rizwan Patel
2
@bobbaluba, você pode especificar o carregador no arquivo de configuração: loaders: [ { test: /\.json$/, loader: 'json' } ]. { test: /\.json$/, loader: 'json' }adiciona o carregador json para arquivos .json, contanto que você tenha instalado o carregador.
Jake de
1
Lembre-se de que se você tentar ser DRY aqui escrevendo uma função que o chame require.context(...)(por exemplo, se você estiver tentando exigir tudo em várias pastas), o webpack irá lançar um aviso. Os argumentos require.contextdevem ser constantes de tempo de compilação.
Isaac Lyman
3
@ Joel npmjs.com/package/require-context foi criado em 2017 e minha resposta é a partir de 2015, por isso é difícil dizer que é levado diretamente de lá. Observe também que require-contexté um invólucro webpacke sua amostra é retirada da webpackdocumentação do em webpack.js.org/guides/dependency-management/#context-module-api - que foi adicionado em 2016 (em github.com/webpack/ webpack.js.org/commit/… ), também depois de escrever minha resposta ... Talvez os redatores do webpack tenham sido influenciados por minha resposta. Observe que eles o expandiram para ter cache.
splintor de
38

No arquivo do meu aplicativo acabei colocando o requerer

require.context(
  "./common", // context folder
  true, // include subdirectories
  /.*/ // RegExp
)("./" + expr + "")

cortesia desta postagem: https://github.com/webpack/webpack/issues/118

Agora está adicionando todos os meus arquivos. Eu tenho um carregador para html e css e parece funcionar muito bem.

ChickenFur
fonte
27
O que está exprneste exemplo?
twiz
2
expré um caminho para um único arquivo dentro da pasta de contexto. portanto, se você precisar fazer isso não apenas para exigir todos os arquivos html, isso é útil. webpack.github.io/docs/context.html#context-module-api
mkoryak
18
ainda não está claro para mim o que o exprargumento baseado está fazendo aqui, mesmo depois de olhar os documentos do webpack. o que significa "fazer isso não apenas para exigir todos os arquivos html"? obrigado
mikkelrd,
3
Recebi ReferenceError não capturado: expr não está definido . Alguma dica sobre isso?
CSchulz
2
Ainda sem resposta sobre o que exprsignifica aqui
wizulus
5

Que tal um mapa de todos os arquivos em uma pasta?

// { 
//   './image1.png':  'data:image/png;base64,iVBORw0KGgoAAAANS',
//   './image2.png':  'data:image/png;base64,iVBP7aCASUUASf892',
// }

Faça isso:

const allFiles = (ctx => {
    let keys = ctx.keys();
    let values = keys.map(ctx);
    return keys.reduce((o, k, i) => { o[k] = values[i]; return o; }, {});
})(require.context('./path/to/folder', true, /.*/));
Steven de Salas
fonte
Obrigado por abandonar este exemplo. Usei map simplesmente retornar o valor que cada um dos meus arquivos exportou =).
cacoder
1

Exemplo de como obter um mapa de todas as imagens na pasta atual.

const IMAGES_REGEX = /\.(png|gif|ico|jpg|jpeg)$/;

function mapFiles(context) {
  const keys = context.keys();
  const values = keys.map(context);
  return keys.reduce((accumulator, key, index) => ({
    ...accumulator,
    [key]: values[index],
  }), {});
}

const allImages = mapFiles(require.context('./', true, IMAGES_REGEX));
Alex K
fonte
1

Todos os méritos para @splintor (obrigado).

Mas aqui está minha própria versão derivada.

Benefícios:

  • Quais módulos exportam são reunidos em um {module_name: exports_obj}objeto.
    • module_name é construído a partir de seu nome de arquivo.
    • ... sem extensão e substituindo barras por sublinhados (no caso de verificação de subdiretório).
  • Comentários adicionados para facilitar a personalização.
    • Ou seja, você pode querer não incluir arquivos em subdiretórios se, digamos, eles estiverem lá para serem solicitados manualmente para módulos de nível raiz .

EDITAR: Se, como eu, você tem certeza de que seus módulos não retornarão nada além de (pelo menos no nível da raiz) um objeto javascript regular, você também pode "montá-los" replicando sua estrutura de diretório original (consulte Código (versão detalhada ) seção no final).

Código (versão original):

function requireAll(r) {
    return Object.fromEntries(
        r.keys().map(function(mpath, ...args) {
            const result =  r(mpath, ...args);
            const name = mpath
                .replace(/(?:^[.\/]*\/|\.[^.]+$)/g, '') // Trim
                .replace(/\//g, '_') // Relace '/'s by '_'s
            ;
            return [name, result];
        })
    );
};
const allModules = requireAll(require.context(
    // Any kind of variables cannot be used here
    '@models'  // (Webpack based) path
    , true     // Use subdirectories
    , /\.js$/  // File name pattern
));

Exemplo:

Saída de amostra para eventual console.log(allModules);:

{
  main: { title: 'Webpack Express Playground' },
  views_home: {
    greeting: 'Welcome to Something!!',
    title: 'Webpack Express Playground'
  }
}

Árvore do diretório:

models
├── main.js
└── views
    └── home.js

Código (versão profunda):

function jsonSet(target, path, value) {
    let current = target;
    path = [...path]; // Detach
    const item = path.pop();
    path.forEach(function(key) {
        (current[key] || (current[key] = {}));
        current = current[key];
    });
    current[item] = value;
    return target;
};
function requireAll(r) {
    const gather = {};
    r.keys().forEach(function(mpath, ...args) {
        const result =  r(mpath, ...args);
        const path = mpath
            .replace(/(?:^[.\/]*\/|\.[^.]+$)/g, '') // Trim
            .split('/')
        ;
        jsonSet(gather, path, result);
    });
    return gather;
};
const models = requireAll(require.context(
    // Any kind of variables cannot be used here
    '@models'  // (Webpack based) path
    , true     // Use subdirectories
    , /\.js$/  // File name pattern
));

Exemplo:

Resultado do exemplo anterior usando esta versão:

{
  main: { title: 'Webpack Express Playground' },
  views: {
    home: {
      greeting: 'Welcome to Something!!',
      title: 'Webpack Express Playground'
    }
  }
}
bitifet
fonte
0

isso funciona para mim:

function requireAll(r) { r.keys().forEach(r); } 

requireAll(require.context('./js/', true, /\.js$/));

NOTA: isso pode exigir arquivos .js em subdiretórios de ./js/ recursivamente.

jack long
fonte