Razzle: expor uma lista de todos os ativos que são seguros para definir `Cache-Control` imutável

Criado em 31 jul. 2020  ·  40Comentários  ·  Fonte: jaredpalmer/razzle

🚀 Solicitação de recurso

Estou investigando a melhor maneira de adicionar um middleware ao meu servidor razzle para detectar sempre que ele serve um arquivo criado pela razzle que inclui um webpack [hash:8] ou [contenthash:8] no nome do arquivo. Discuti primeiro alguns dos problemas que estou encontrando aqui https://github.com/jaredpalmer/razzle/pull/1368#issuecomment -664015050

Gostaria que o razzle gerasse e exponha a lista de arquivos / ativos seguros para serem considerados "imutáveis" (para fins de definição de Cache-Control cabeçalhos nas respostas) de uma forma que seja fácil de consumir sem transformação extra do arquivos chunks.json e / ou assets.json

NOTA: ao definir respostas de controle de cache de longa duração e imutáveis, quero evitar fazer qualquer tipo de "aproximação" sobre se um arquivo pode ser considerado imutável (também conhecido como regex para detectar um hash no nome do arquivo) porque um falso positivo pode levar a um arquivo sendo armazenado em cache imutavelmente por um longo tempo e não seria corrigível por uma invalidação de cache do lado do servidor, o que pode ser um problema muito doloroso de se solucionar.

Comportamento Atual

TL; DR de por que é difícil tentar usar os arquivos json expostos no momento:

  • Para obter a lista concreta de todos os arquivos que são seguros para armazenar em cache de forma imutável (porque eles têm hashes de construção ou conteúdo), preciso usar chunks.json e assets.json . chunks.json inclui arquivos de mapa de origem e assets.json tem arquivos como png / fonts etc que chunks.json não tem.
  • Os ativos.json e chunks.json não estão no mesmo formato (este é possivelmente um problema que se manifesta para mim porque eu deixo o webpack dividir as coisas em vários pedaços), portanto, exigem transformações ad-hoc diferentes para agrupar a lista completa de todos os arquivos /ativos. Algumas das diferenças são:

    • Parece que para qualquer pedaço que não está em (assets.json).client (por exemplo: "client": { "js": "/static/js/bundle.6fc534aa.js" } ), assets.json agrupa todos os outros ativos em uma string vazia (por exemplo: "": { "js": "/static/js/0.cb47cee9.chunk.js" } ).

    • se apenas um arquivo estiver presente em um grupo chunks.json, será uma matriz com um item (por exemplo: "client": { "css": ["filename.css"] } ), se houver apenas um arquivo presente em assets.json, será apenas o string única (por exemplo: "client": { "css": "filename.css" } ).

  • Meu assets.json atualmente contém "json": "/../chunks.json" que não é algo que eu acho que deveria estar lá (não tenho certeza se isso é um bug ou não), mas tenho que remover isso manualmente ao fazer a lista de arquivos que podem receber cabeçalhos de resposta de controle de cache de longa duração.
  • O plano de adicionar um array chunks: ["1", "2", "3"] ao chunks.json é um tanto chato porque significa que tenho que fazer um trabalho extra para filtrar (chunks.json).client.chunks porque ele não contém um array de arquivos como (chunks.json).client.css e (chunks.json).client.js etc.
  • Antes da mudança que fiz aqui, os arquivos que não estavam no pedaço client nem apareciam no arquivo chunks.json . Eu fiz / sugeri a alteração para alterá-lo para usar o (s) número (s) do bloco como chave, porque pelo menos eles aparecem no arquivo. A desvantagem disso é que agora chunks.json e assets.json diversificam ainda mais em seu esquema ao lidar com blocos que não são o bloco nomeado primário ( "client": {/* blah */ } ).

usando assets.json e chunks.json

Atualmente usando o assets.json e chunks.json, isso é o que tive que fazer até agora

Eu não tenho:

  • adicionado carregando o assets.json ainda e resolvendo diferenças entre os formatos
  • Filtrando arquivos / campos no json que eu sei que não deveriam estar lá, como "chunks": ["1", "2", "3"] e "json": "/../chunks.json"
function razzleCacheableFiles() {
  // TODO: Add loading the assets.json file to support (png/txt files etc)

  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  const chunks = require(process.env.RAZZLE_CHUNKS_MANIFEST!);
  const filesByType = Object.entries(chunks).reduce(
    (chunkAcc: any, [, chunk]) => {
      const types = Object.entries(chunk as any).reduce(
        (typeAcc, [fileType, files]) => {
          return {
            [fileType]: chunkAcc[fileType]
              ? [...chunkAcc[fileType], ...(files as string[])]
              : files,
          };
        },
        {},
      );
      return types;
    },
    {},
  );
  const files = Object.entries(filesByType).reduce(
    (acc: any[], [, files]) => [...acc, ...(files as string[])],
    [],
  );
  return files;
}

const cacheableFiles = razzleCacheableFiles();

// Serve static files located under `process.env.RAZZLE_PUBLIC_DIR`
const assetCaching = {
  immutable: {
    maxAge: CacheFor.OneMonth,
    sMaxAge: CacheFor.OneYear,
  },
  default: {
    maxAge: CacheFor.OneDay,
    sMaxAge: CacheFor.OneWeek,
  }
};
app.use(
  serve(process.env.RAZZLE_PUBLIC_DIR!, {
    setHeaders(res, path) {
      const filename = path.replace(process.env.RAZZLE_PUBLIC_DIR!, "");
      const hasHashInFilename = cacheableFiles.includes(filename);
      if (hasHashInFilename) {
        const { immutable } = assetCaching;
        res.setHeader(
          "Cache-Control",
          `max-age=${immutable.maxAge},s-maxage=${immutable.sMaxAge},immutable`,
        );
        return;
      }
      res.setHeader(
        "Cache-Control",
        `max-age=${assetCaching.default.maxAge},s-maxage=${asetCaching.default.sMaxAge}`,
      );
    },
  }),
);

Uma descrição clara e concisa de qual é o comportamento / uso atual.

Comportamento desejado

Provavelmente haveria muitas maneiras de fazer isso, mas a principal coisa que eu quero é apenas uma maneira de carregar uma matriz de todos os ativos armazenáveis ​​/ imutáveis ​​gerados pelo razzle build. o resultado pode ser parecido com este:

// File: caching.json
// contains all files/assets with a hash in them regardless of what type of file they are.
{
  "immutable": [
    "/static/js/0.cb47cee9.chunk.js",
    "/static/js/0.cb47cee9.chunk.js.map",
    "/static/js/0.cb47cee9.chunk.js.LICENSE.txt",
    "/static/media/ferris-error.407b714e.png"
  ],
  // I'm not even sure if this is required because I don't think razzle generates any files that don't have hashes in them?
  // possibly files copied in from the `public/` directory during build. but I'm not even sure if it'd that'd useful
  "standard": []
}
// RAZZLE_CACHING_MANIFEST is probably a silly name but 
const cacheableFiles = require(process.env.RAZZLE_CACHING_MANIFEST!);

// Serve static files located under `process.env.RAZZLE_PUBLIC_DIR`
const assetCaching = {
  immutable: {
    maxAge: CacheFor.OneMonth,
    sMaxAge: CacheFor.OneYear,
  },
  default: {
    maxAge: CacheFor.OneDay,
    sMaxAge: CacheFor.OneWeek,
  }
};
app.use(
  serve(process.env.RAZZLE_PUBLIC_DIR!, {
    setHeaders(res, path) {
      const filename = path.replace(process.env.RAZZLE_PUBLIC_DIR!, "");
      const hasHashInFilename = cacheableFiles.immutable.includes(filename);
      if (hasHashInFilename) {
        const { immutable } = assetCaching;
        res.setHeader(
          "Cache-Control",
          `max-age=${immutable.maxAge},s-maxage=${immutable.sMaxAge},immutable`,
        );
        return;
      }
      res.setHeader(
        "Cache-Control",
        `max-age=${assetCaching.default.maxAge},s-maxage=${asetCaching.default.sMaxAge}`,
      );
    },
  }),
);

Solução sugerida

Não investiguei totalmente qual seria uma boa solução, mas depois de tentar reunir esta lista de "ativos armazenáveis ​​em cache" em tempo de execução usando assets.json e chunks.json , estou bastante convencido de que em um No mínimo, a melhor maneira de fazer isso seria em tempo de construção com algum tipo de plugin do webpack e contornar as inconsistências desses dois arquivos.

Para meus propósitos, provavelmente, inicialmente começarei a estudar como fazer isso com um plug-in em vez de em tempo de execução como venho fazendo, mas acho que haveria um valor significativo em ter isso integrado ao Razzle por padrão. Ser capaz de definir o controle de cache de longa duração em arquivos com hash é em grande parte o motivo pelo qual eles são hash, para começar, então, expor uma lista de todos esses arquivos parece apropriado.

Quem causa esse impacto? Para quem é isso?

Quaisquer usuários que desejam definir cabeçalhos de resposta de controle de cache imutáveis ​​e de longa duração apropriados para arquivos gerados e hash pelo razzle.

Descreva as alternativas que você considerou

  • Gere uma lista de todos os arquivos imutáveis ​​/ armazenáveis ​​em cache em tempo de execução, juntando chunks.json e assets.json (parece sujeito a erros e frágil).
  • Crie um plugin externo para pré-gerar a lista de arquivos armazenáveis ​​em cache no momento da construção. (parece possivelmente frágil em versões razzle para um recurso que parece que deveria ser incorporado / estável)
  • Adicione-o como um plugin interno para a configuração padrão do razzle e exponha uma maneira de acessá-lo por padrão, por exemplo: require(process.env.RAZZLE_CACHING_MANIFEST!) . ()

Contexto adicional

Eu estaria disposto a ajudar / contribuir para fazer essa mudança, mas posso precisar de um ponto de vista na direção certa (e, claro, se essa é ou não uma mudança que seria aceita / bem-vinda).

Além disso, ter algo como isso pode tornar mais fácil ter alguns testes / estabilidade para garantir que as coisas estão usando [contenthash:8] vez de [hash:8] (criar hash) se / quando eles podem https: / /github.com/jaredpalmer/razzle/issues/1331

discussion enhancement help wanted razzle webpack webpack-config

Comentários muito úteis

Lançou um canário agora :)

Todos 40 comentários

Parece uma ideia que vale a pena.

Ele também está conectado a outro problema que é a configuração do chunkGroup na otimização. Porque se isso fosse configurado, a string vazia seria "shared", "framework" etc.

Se você olhar para next.js, eles usam uma configuração chunkGroups como esta.

Quando mudarmos isso, também será incompatível com as versões anteriores, mas isso tem que ser feito. Tenho mais algumas grandes mudanças em andamento que também precisam de um grande lançamento.

Mas sinta-se à vontade para criar um código que resolva isso 😀

Se você olhar para next.js, eles usam uma configuração chunkGroups como esta.

Legal, não entendi se / como outras ferramentas / frameworks abordam isso, você tem um link / exemplos?

Também está conectado a outro problema que é a configuração do chunkGroup na otimização

Um problema aberto do Razzle? você poderia me indicar qual deles para que eu possa ter mais contexto 😄

Eu acho que uma forma potencial de resolver isso é definir mais fortemente a forma / esquema dos chunks.json e assets.json . Provavelmente precisaria ser considerado com cuidado (e ter uma grande versão do solavanco), mas se houver exemplos de como outros frameworks etc resolveram o problema, pode fazer sentido seguir uma direção semelhante

1361 # 1370

E

https://github.com/vercel/next.js/blob/canary/packages/next/build/webpack-config.ts#L378

Não tenho certeza sobre como eles fazem o manifesto.

Dê uma olhada em https://github.com/jaredpalmer/razzle/issues/1377 agora, adicionado um novo exemplo :)

@fivethreeo Não consegui dedicar mais tempo especificamente a esse problema 😅, com certeza vou tentar passar algum tempo testando o pré-lançamento da v4. Se você acha que está pronto para isso, espero tentar testá-lo nos próximos dias.

Não tenho certeza se é de muito interesse, mas tornei o que estou trabalhando público agora aqui .

Estou muito interessado na v4 porque, com sorte, significa que posso remover o máximo de substituições de "plug-in" necessárias para definir as coisas aqui , especialmente para o texto digitado.

O material armazenável em cache está aqui .

Todos os arquivos usam contenthash agora. Os arquivos copiados, eu diria que são uma prática ruim quando temos um bundler.

Não tenho certeza se entendi o que você quis dizer com "Os arquivos copiados, eu diria que são uma má prática quando temos um bundler".

Atualmente, o comportamento é que se você colocar quaisquer arquivos na pasta de nível superior public/ em seu projeto razzle.

build/
public/
  robots.txt
  manifest.json
package.json

Eles são eliminados nos ativos estáticos quando você razzle build

build/
  public/
    robots.txt
    manifest.json
    static/
      ...
public/
  robots.txt
  manifest.json
package.json

Eu estava pensando que poderia ser desejável manter uma lista de todos os bens que foram copiados-in durante a compilação por isso eles são especificamente targettable separadamente para aplicar cache-controle para.

Acho que o argumento contra isso (que eu posso pensar) seria que pode não haver necessidade do usuário distinguir entre os arquivos que o razzle copiou durante a compilação e aqueles que podem ter sido colocados manualmente fora do razzle build

Acho que public deve conter apenas robots.txt e favicon.ico e eles não serão versionados por hashes.

Qualquer outra coisa deve ser empacotada pelo webpack. Quaisquer favicons maiores devem ser agrupados.

Talvez, mas mesmo se você quiser manter a compatibilidade "plug and play" com um create-react-app padrão, pode valer a pena considerar que o manifesto do aplicativo e alguns ícones também estarão presentes lá .

Lembro-me vivamente de que há razões pelas quais manifest.json / manifest.webmanifest não deve conter um hash de construção, que é uma das razões pelas quais é frequentemente excluído do processamento pelo empacotador. Posso estar errado / não lembrar, mas possivelmente algo a ver com PWAs e modo offline

Algum dos projetos de exemplo da razzle implementa suporte PWA (e / ou service worker)?

Talvez menos relevantes, mas algumas outras coisas que coloquei na pasta public/ no passado, ao usar criar-reagir-app, são arquivos para download relacionados ao site, mas onde URLs persistentes são necessários. Como ter um documento PDF que pode ser vinculado ao enviar e-mails, etc. 🤷

Tentando procurar exemplos de se / por que / quando os manifestos da web devem ser separados do empacotador:

https://stackoverflow.com/questions/54145027/what-happens-when-you-add-a-version-hash-to-your-service-worker-file

Há um comentário nessa postagem que leva a https://github.com/w3c/manifest/issues/446#issuecomment -351368893

Sim, os arquivos para download devem ir para lá. Hm, mas como adicionamos esses arquivos ao assets.json? Alguma ideia? 😀Devemos fazer o webpack encontrá-los e agrupá-los como estão? Modificar o assets.json parece hackear.

Não acho que exista um exemplo de PWA. Mas se eles precisam de um nome consistente. Isso precisa ser tratado pelo webpack.

Vou substituir o plugin de ativos pelo plugin de manifesto para que possamos adaptar a saída.

Adicionado um novo manifesto de ativos com todos os arquivos https://github.com/jaredpalmer/razzle/commit/1c6e9169e9d8eee256d0f118f8a88da8de85989f alguma sugestão de melhorias?

Lançou um canário agora :)

Vejo que o plugin de manifesto não é realmente mantido. O melhor seria fazer o nosso. Mas atualmente não conheço ninguém além (talvez) de mim ou do pessoal do webpack que pode fazer isso.

Adicionado ao ramo de canário agora. Uma espécie de hack por enquanto. Mas funciona e é um começo que pode ser melhorado.

Depois de algumas considerações, não irei adicionar isso ao núcleo.

Mas aqui está o código que criei:

        new ManifestPlugin({
          fileName: path.join(paths.appBuild, 'assets.json'),
          writeToFileEmit: true,
          generate: (seed, files) => {
            const entrypoints = new Set();
            const noChunkFiles = new Set();
            const longTermCacheFiles = new Set();
            files.forEach(file => {
              if (file.isChunk) {
                const groups = (
                  (file.chunk || {})._groups || []
                ).forEach(group => entrypoints.add(group));
              } else {
                noChunkFiles.add(file);
              }
              if (!webpackOptions.fileLoaderExclude.some(re=>re.test(file.path))) {
                let fileHasHash = /\[(build|content)?hash/.test(
                  typeof webpackOptions.fileLoaderOutputName == 'function' ?
                  webpackOptions.fileLoaderOutputName(file) : webpackOptions.fileLoaderOutputName);
                if (fileHasHash) longTermCacheFiles.add(file);
              } else if (webpackOptions.urlLoaderTest.some(re=>re.test(file.path))) {
                let urlHasHash = /\[(build|content)?hash/.test(
                  typeof webpackOptions.urlLoaderOutputName == 'function' ?
                  webpackOptions.urlLoaderOutputName(file) : webpackOptions.urlLoaderOutputName);
                if (urlHasHash) longTermCacheFiles.add(file);
              } else if (webpackOptions.cssTest.some(re=>re.test(file.path))) {
                let cssHasHash = /\[(build|content)?hash/.test(
                  typeof webpackOptions.cssOutputFilename == 'function' ?
                  webpackOptions.cssOutputFilename(file) : webpackOptions.cssOutputFilename);
                if (cssHasHash) longTermCacheFiles.add(file);
              } else if (webpackOptions.jsTest.some(re=>re.test(file.path))) {
                let jsHasHash = /\[(build|content)?hash/.test(
                  typeof webpackOptions.jsOutputFilename == 'function' ?
                  webpackOptions.jsOutputFilename(file) : webpackOptions.jsOutputFilename);
                if (jsHasHash) longTermCacheFiles.add(file);
              }
            });
            const entries = [...entrypoints];
            const entryArrayManifest = entries.reduce((acc, entry) => {
              const name =
                (entry.options || {}).name ||
                (entry.runtimeChunk || {}).name ||
                entry.id;
              const allFiles = []
                .concat(
                  ...(entry.chunks || []).map(chunk =>
                    chunk.files.map(path => config.output.publicPath + path)
                  )
                )
                .filter(Boolean);

              const filesByType = allFiles.reduce((types, file) => {
                const fileType = file.slice(file.lastIndexOf('.') + 1);
                types[fileType] = types[fileType] || [];
                types[fileType].push(file);
                return types;
              }, {});

              const chunkIds = [].concat(
                ...(entry.chunks || []).map(chunk => chunk.ids)
              );

              return name
                ? {
                    ...acc,
                    [name]:  { ...filesByType, chunks: chunkIds },
                  }
                : acc;
            }, seed);
            entryArrayManifest['noentry'] = [...noChunkFiles]
              .map(file => file.path)
              .reduce((types, file) => {
                const fileType = file.slice(file.lastIndexOf('.') + 1);
                types[fileType] = types[fileType] || [];
                types[fileType].push(file);
                return types;
              }, {});
              entryArrayManifest['cacheable'] = [...longTermCacheFiles]
                .map(file => file.path);
            return entryArrayManifest;
          },
        })

Mas aprendi muito sobre ativos;)

Desculpe, não tenho conseguido gastar muito tempo com isso há algum tempo, mas parece legal. Estou dando uma olhada agora em como atualizar meu material para a última versão estável do Razzle e testando sua sugestão como um plugin personalizado.

Parece muito bom, mas estou um pouco confuso sobre isso:

let fileHasHash = /\[(build|content)?hash/.test(
  typeof webpackOptions.fileLoaderOutputName == 'function'
    ? webpackOptions.fileLoaderOutputName(file)
    : webpackOptions.fileLoaderOutputName);

if (fileHasHash) longTermCacheFiles.add(file);

O que webpackOptions.fileLoaderOutputName pretende ser? para mim, sempre parece apenas indefinido.

Apenas em razzle canário

Legal, fiz alguns progressos agora com a obtenção de um galho em meu projeto para trabalhar no galho de canário. Não está funcionando bem, no momento meus problemas parecem estar principalmente relacionados com a configuração do carregador do babel para reconhecer pacotes irmãos. Consigo construir, mas consigo problemas de "não consigo encontrar o módulo" quando tento executá-lo.

Isso provavelmente não é muito interessante / útil, mas:

https://github.com/bootleg-rust/sites/pull/2/files

da memória, originalmente peguei emprestada a configuração de https://github.com/jaredpalmer/razzle/issues/664

/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/lib-ssr-runtime sync:2
        var e = new Error("Cannot find module '" + req + "'");
         ^
Error: Cannot find module 'undefined'
    at require (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/lib-ssr-runtime sync:2:10)
    at razzleCacheableFiles (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/lib-ssr-runtime/server.tsx:106:18)
    at createKoaApp (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/lib-ssr-runtime/server.tsx:61:26)
    at Module.call (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/src/server.tsx:42:13)
    at a (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/webpack/bootstrap:19:22)
    at Object.call (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/server.js:1:31123)
    at __webpack_require__ (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/webpack/bootstrap:19:22)
    at /Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/webpack/bootstrap:83:10
    at Object.<anonymous> (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/server.js:1:935)

https://github.com/jaredpalmer/razzle/issues/1459

e defina NODE_PATH = .. / ou algo assim

Ok, investigando um pouco mais, acabei de perceber que o problema está apenas fazendo com que process.env.RAZZLE_CHUNKS_MANIFEST não seja mais definido 😅.

A única coisa que eu estava usando era para detectar quais ativos podiam ser armazenados em cache, então parece que devo ser capaz de fornecer a nova configuração ManifestPlugin você vinculou uma chance agora para substituí-la 🎉.

OK!

Fiz um plugin personalizado em meu projeto que parece funcionar bem o suficiente para o meu caso de uso por enquanto. O código que você criou foi muito útil, tendo isso como ponto de partida.

Eu mudei um pouco, mas para sua informação, acho que há um problema onde ele pensa que tudo é processado por file-loader porque ele usa Array.prototype.every() vez de Array.prototype.some() : !webpackOptions.fileLoaderExclude.every(re=>re.test(file.path))

Caso seja útil compartilhar aqui:

function modifyWebpackConfig({
  env: { target, dev },
  webpackConfig,
  webpackObject,
  options: { pluginOptions, razzleOptions, webpackOptions },
  paths,
}) {
  // TODO: allow passing in extra file categorizers with `pluginOptions`
  const fileCategorizers = [
    {
      test: webpackOptions.urlLoaderTest,
      outputName: webpackOptions.urlLoaderOutputName,
    },
    {
      test: webpackOptions.cssTest,
      outputName: webpackOptions.cssOutputFilename,
    },
    {
      test: webpackOptions.jsTest,
      outputName: webpackOptions.jsOutputFilename,
    },
    {
      exclude: webpackOptions.fileLoaderExclude,
      outputName: webpackOptions.fileLoaderOutputName,
    },
  ];

  const fileName = path.join(paths.appBuild, "cacheable-assets.json");
  const assetPlugin = new WebpackManifestPlugin({
    fileName,
    writeToFileEmit: true,
    generate: (seed, files) => {
      const notHashedFiles = new Set();
      const hashedFiles = new Set();

      const setFileAs = (file, { containsHash }) => {
        if (containsHash) {
          hashedFiles.add(file);
        } else {
          notHashedFiles.add(file);
        }
      };

      files.forEach((file) => {
        if (file.name.startsWith("..")) {
          // Files that start with ".." will live outside of the public/
          // folder and therefore can't/shouldn't be accessed.
          return;
        }

        const fileCategorized = fileCategorizers.some(
          ({ test, exclude, outputName }) => {
            const passesTest =
              test != null ? fileMatchesAnyRegexp(file, test) : true;

            const passesExclude =
              exclude != null ? !fileMatchesAnyRegexp(file, exclude) : true;

            const fileMatches =
              passesTest &&
              passesExclude &&
              fileMatchesTemplate(file.path, outputName);

            if (fileMatches) {
              const containsHash = webpackLoaderOutputContainsHash(
                outputName,
                file,
              );

              setFileAs(file, { containsHash });
            }

            return fileMatches;
          },
        );

        if (!fileCategorized) {
          // TODO: allow "strict" vs "lazy" mode here where we can only use
          // regex on the filename to guess if a file contains a hash in it.
          setFileAs(file, { containsHash: false });
        }
      });

      const mutable = [...notHashedFiles].map((file) => file.path);
      const immutable = [...hashedFiles].map((file) => file.path);
      return {
        mutable,
        immutable,
      };
    },
  });

  if (target === "web") {
    webpackConfig.plugins.push(assetPlugin);
  }

  if (target === "node") {
    // NOTE: adding multiple DefinePlugin's causes issues
    // so we have to find and edit the existing one.
    const definePlugin = webpackConfig.plugins.find(
      (p) => p.constructor.name === "DefinePlugin",
    );
    definePlugin.definitions[
      "process.env.RAZZLE_PLUGIN_CACHEABLE_ASSETS"
    ] = JSON.stringify(fileName);
  }

  return webpackConfig;
}

const cacheableAssetsPlugin = {
  modifyWebpackConfig,
};

Ou pode dar uma olhada aqui https://github.com/bootleg-rust/sites/pull/2/files#diff -59ee436c0396a1f925f067b7e7cbcdee354003236a279e0a87cf8831c7f587e3

Ah certo, sim, obrigado. Ainda estou me acostumando com os novos ganchos do plugin, gostei 🎉!

Acho que o único problema principal que ainda estou tendo e que não consegui resolver é que por algum motivo o scss plugin / carregador não funciona quando executado no modo de desenvolvimento usando razzle start mas se eu fizer um razzle build completo, tudo parecerá bem.

Alguma ideia do que isso possa ser? ou vale a pena colocar isso em um problema diferente do github em algum lugar?

Use também modifyPaths para caminhos personalizados para que possam ser compostos.

Não funciona como?

Pode ser um novo problema .. :)

Esqueça, o sass loader não funcionando não era nada específico com o Razzle. algo a ver com uma incompatibilidade de versão ou algo com a versão de react-scripts e / ou livro de histórias que eu tinha em um pacote irmão que estava carregando dependências.

Adicionados ganchos para manuseio de ativos, fechamento 😀

Vejo que você adicionou um plugin externo. Eu ainda preciso consertar isso para cliente / servidor / sem servidor. Tem alguma ideia para isso no canário? Um pouco preso.

Os ganchos que você usa agora.

Vejo que você adicionou um plugin externo. Eu ainda preciso consertar isso para cliente / servidor / sem servidor. Tem alguma ideia para isso no canário? Um pouco preso.

Definitivamente, achei muito conveniente (principalmente no servidor) agrupar por padrão todos os node_modules em build/server.js . Ser capaz de excluir a pasta node_modules inteiramente de minhas imagens do docker de produção parece muito bom.

Dito isso, não tive a necessidade de usar / testar como funciona com quaisquer dependências nativas / específicas da plataforma (tenho a sensação de que coisas como imagemagick teriam problemas)

Meu processo de pensamento geral com o plugin "externo" que fiz é:

const externalsPluginOptions = {
  // By default the NodeJS process uses the externals function razzle has and relies on `node_modules` to still be available
  // after performing a razzle build. Setting this to `true` would mean that all dependencies attempt to get bundled into
  // the build/ output unless explicitly specified as an external
  resetNodeExternals: false,
  // This probably wouldn't actually be required because the browser runtime
  // doesn't have externals by default (i'm assuming)
  resetWebExternals: true,

  webExternals: [
    // add externals for the web runtime here
  ],
  nodeExternals: [
    // add externals for the node runtime here
  ],
};

Para ser honesto, antes de decidir por uma API de configuração "adequada" para isso (particularmente se fosse no razzle core), provavelmente teria que ler a documentação do webpack para externals com um pouco mais de profundidade no diferentes casos de uso para externos 😅.

No momento, estou apenas usando-o para redefinir os externos para que fiquem vazios para que eu tenha tudo empacotado em um aplicativo facilmente portátil que não depende de node_modules em tempo de execução

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

panbanda picture panbanda  ·  5Comentários

jcblw picture jcblw  ·  4Comentários

howardya picture howardya  ·  5Comentários

knipferrc picture knipferrc  ·  5Comentários

mhuggins picture mhuggins  ·  3Comentários