Razzle: exposer une liste de tous les actifs qui sont sûrs de définir un `Cache-Control` immuable

Créé le 31 juil. 2020  ·  40Commentaires  ·  Source: jaredpalmer/razzle

🚀 Demande de fonctionnalité

J'ai étudié la meilleure façon d'ajouter un middleware à mon serveur razzle pour détecter chaque fois qu'il sert un fichier construit par razzle qui inclut un pack web [hash:8] ou [contenthash:8] dans le nom de fichier. J'ai d'abord discuté de certains des problèmes que je rencontre ici https://github.com/jaredpalmer/razzle/pull/1368#issuecomment -664015050

Je voudrais que razzle génère et expose la liste des fichiers / actifs sûrs pour être considérés comme "immuables" (aux fins de la définition des en-têtes Cache-Control dans les réponses) d'une manière qui soit facile à consommer sans transformation supplémentaire du fichiers chunks.json et / ou assets.json

REMARQUE: lors de la définition de réponses de contrôle de cache à longue durée de vie et immuables, je veux éviter de faire toute sorte d '"approximation" pour savoir si un fichier peut être considéré comme immuable (AKA regex pour détecter un hachage dans le nom de fichier) car un faux positif peut conduire à un fichier étant immuablement mis en cache pendant une longue période et ne serait pas réparable par une invalidation de cache côté serveur, ce qui peut être un problème très pénible à contourner.

Comportement actuel

TL; DR de la raison pour laquelle il est difficile d'utiliser les fichiers json actuellement exposés:

  • Afin d'obtenir la liste concrète de tous les fichiers qui peuvent être mis en cache immuablement (car ils contiennent des hachages de compilation ou de contenu), je dois utiliser à la fois chunks.json et assets.json . chunks.json inclut des fichiers de plan de source et assets.json a des fichiers comme png / fonts, etc., ce que chunks.json n'a pas.
  • Les assets.json et chunks.json ne sont pas dans le même format (c'est peut-être un problème qui se manifeste pour moi parce que je laisse webpack diviser les choses en plusieurs morceaux) donc nécessitent une transformation ad-hoc différente pour rassembler la liste complète de tous les fichiers /les atouts. Certaines des différences sont:

    • Il semble que pour tout morceau qui n'est pas dans (assets.json).client (par exemple: "client": { "js": "/static/js/bundle.6fc534aa.js" } ), assets.json regroupe tous les autres actifs sous une chaîne vide (par exemple: "": { "js": "/static/js/0.cb47cee9.chunk.js" } ).

    • si un seul fichier est présent dans un groupe chunks.json, ce sera un tableau avec un élément (par exemple: "client": { "css": ["filename.css"] } ), s'il n'y a qu'un seul fichier présent dans assets.json, ce sera à la place juste le chaîne unique (par exemple: "client": { "css": "filename.css" } ).

  • Mon assets.json contient actuellement "json": "/../chunks.json" ce qui, à mon avis, ne devrait pas y figurer (je ne suis pas sûr qu'il s'agisse d'un bogue ou non) mais je dois le supprimer manuellement lors de la création de la liste de fichiers qui peuvent recevoir des en-têtes de réponse de contrôle de cache de longue durée.
  • Le plan pour ajouter un tableau chunks: ["1", "2", "3"] au chunks.json est quelque peu ennuyeux car cela signifie que je dois faire un travail supplémentaire pour filtrer le (chunks.json).client.chunks car il ne contient pas un tableau de fichiers comme (chunks.json).client.css et (chunks.json).client.js etc.
  • Avant la modification que j'ai apportée ici, les fichiers qui ne sont pas dans le bloc client n'apparaissaient même pas dans le fichier chunks.json . J'ai fait / suggéré le changement pour le changer en utilisant le (s) numéro (s) de bloc comme clé, car au moins ils apparaissent alors dans le fichier. L'inconvénient est que maintenant chunks.json et assets.json diversifient davantage dans leur schéma lorsqu'ils traitent des blocs qui ne sont pas le bloc nommé principal ( "client": {/* blah */ } ).

en utilisant assets.json et chunks.json

En utilisant actuellement les assets.json et chunks.json, c'est ce que j'ai dû faire à peu près jusqu'à présent

Je n'ai pas:

  • ajouté le chargement du fichier assets.json et la résolution des différences entre les formats
  • Filtrer les fichiers / champs dans le json dont je sais qu'ils ne sont pas censés être là comme "chunks": ["1", "2", "3"] et "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}`,
      );
    },
  }),
);

Une description claire et concise de ce qu'est le comportement / l'utilisation actuelle.

Comportement désiré

Il y aurait probablement de nombreuses façons de le faire, mais la principale chose que je veux est juste un moyen de charger un tableau de tous les actifs pouvant être mis en cache / immuables générés par razzle build. le résultat pourrait ressembler à ceci:

// 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}`,
      );
    },
  }),
);

Solution suggérée

Je n'ai pas complètement étudié ce que serait une bonne solution, mais après avoir essayé de rassembler cette liste d '"actifs pouvant être mis en cache" au moment de l'exécution en utilisant assets.json et chunks.json je suis assez convaincu qu'en un au minimum, la meilleure façon d'accomplir cela serait au moment de la construction avec une sorte de plugin webpack et de contourner les incohérences de ces deux fichiers.

Pour mes besoins, je vais probablement commencer par chercher comment y parvenir avec un plugin plutôt qu'avec au moment de l'exécution, comme je l'ai fait, mais je pense qu'il y aurait une valeur significative à avoir ce baked-in pour razzle par défaut. Être capable de définir un contrôle de cache de longue durée sur les fichiers hachés est en grande partie la raison pour laquelle ils sont hachés au début, donc exposer une liste de tous ces fichiers semble approprié.

Qui a cet impact? À qui est-ce destiné?

Tous les utilisateurs qui souhaitent définir des en-têtes de réponse de contrôle de cache à long terme et immuables appropriés pour les fichiers générés et hachés par razzle.

Décrivez les alternatives que vous avez envisagées

  • Générez une liste de tous les fichiers immuables / pouvant être mis en cache au moment de l'exécution en mélangeant chunks.json et assets.json (semble sujet aux erreurs et fragile).
  • Créez un plugin externe pour pré-générer la liste des fichiers pouvant être mis en cache au moment de la construction. (semble peut-être fragile dans les versions razzle pour une fonctionnalité qui semble devoir être intégrée / stable)
  • Ajoutez-le en tant que plugin interne à la configuration par défaut de razzle et exposez un moyen d'y accéder par défaut, par exemple: require(process.env.RAZZLE_CACHING_MANIFEST!) . ()

Contexte supplémentaire

Je serais prêt à aider / contribuer à faire ce changement, mais je pourrais avoir besoin d'un peu d'un point dans la bonne direction (et bien sûr s'il s'agit ou non d'un changement qui serait accepté / bienvenu).

Aussi une pensée, avoir quelque chose comme ça pourrait faciliter les tests / stabilité pour s'assurer que les choses utilisent [contenthash:8] au lieu de [hash:8] (build hash) si / quand ils peuvent https: / /github.com/jaredpalmer/razzle/issues/1331

discussion enhancement help wanted razzle webpack webpack-config

Commentaire le plus utile

A fait une version Canary maintenant :)

Tous les 40 commentaires

Cela semble être une bonne idée.

Il est également lié à un autre problème qui est la configuration de chunkGroup dans l'optimisation. Parce que si cela était configuré, la chaîne vide serait «partagée», «cadre», etc.

Si vous regardez next.js, ils utilisent une configuration chunkGroups comme celle-ci.

Lorsque nous changerons cela, ce sera également incompatible avec les versions antérieures, mais cela doit être fait. J'ai d'autres changements importants qui nécessitent également une version majeure.

Mais n'hésitez pas à proposer du code qui résout ce problème 😀

Si vous regardez next.js, ils utilisent une configuration chunkGroups comme celle-ci.

Oh cool, je ne suis pas sur si / comment d'autres outils / cadres abordent cela, avez-vous un lien / des exemples?

Il est également lié à un autre problème qui est la configuration de chunkGroup dans l'optimisation

Un problème de razzle ouvert? pourriez-vous m'indiquer lequel pour que je puisse avoir plus de contexte 😄

Je pense qu'une manière potentielle de résoudre ce problème est de définir plus fortement la forme / le schéma des chunks.json et assets.json existants. Il faudrait probablement un examen attentif (et une modification majeure de la version), mais s'il existe des exemples de la façon dont d'autres frameworks, etc. ont résolu le problème, il pourrait être judicieux de suivre une direction similaire

1361 # 1370

Et

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

Je ne sais pas comment ils font le manifeste.

Jetez un œil à https://github.com/jaredpalmer/razzle/issues/1377 maintenant, ajoutez un nouvel exemple :)

@fivethreeo Je n'ai pas réussi à passer plus de temps sur ce problème en particulier 😅, je vais certainement essayer de passer du temps à essayer la version préliminaire de la v4. Si vous pensez qu'il est prêt, j'espère que j'essaierai de l'essayer au cours des prochains jours.

Je ne sais pas si elle est d' un grand intérêt , mais je l' ai fait ce que je travaille sur des partenariats public maintenant ici .

Je suis assez enthousiaste pour la v4 parce que cela signifie, espérons-le, que je peux supprimer autant de remplacements de "plugins" que je dois définir les choses ici , en particulier pour les scripts dactylographiés.

Les éléments d'actifs pouvant être mis en cache sont ici .

Tous les fichiers utilisent maintenant contenthash. Je dirais que les fichiers copiés sont une mauvaise pratique lorsque nous avons un bundler.

Je ne suis pas sûr de comprendre ce que vous entendez par "Les fichiers copiés, je dirais, sont une mauvaise pratique lorsque nous avons un bundler".

Actuellement, le comportement est que si vous placez des fichiers dans le dossier public/ niveau supérieur de votre projet razzle.

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

Ils sont intégrés dans les actifs statiques lorsque vous razzle build

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

Je pensais qu'il serait peut- être souhaitable de maintenir une liste de tous les actifs qui ont été copiés pendant la construction afin qu'ils soient spécifiquement ciblés séparément pour appliquer le contrôle du cache.

Je pense que l'argument contre cela (auquel je peux penser) serait qu'il pourrait ne pas être nécessaire pour l'utilisateur de faire la distinction entre les fichiers que razzle a copiés pendant la construction et ceux qui auraient pu être placés manuellement en dehors du razzle build

Je pense que public ne devrait contenir que robots.txt et favicon.ico et ils ne seront pas versionnés par des hachages.

Tout le reste doit être regroupé par webpack. Toutes les favicons plus grandes doivent être regroupées.

Peut-être, mais même si vous voulez maintenir la compatibilité "plug and play" avec un create-react-app par défaut, cela peut valoir la peine de considérer que le manifeste de l'application et certaines icônes seront également présents .

Je me souviens très bien qu'il y avait des raisons pour lesquelles manifest.json / manifest.webmanifest ne devrait pas contenir de hachage de construction, ce qui est l'une des raisons pour lesquelles il est assez souvent exclu du traitement par le bundler. Je me trompe peut-être / je me souviens mal, mais peut-être quelque chose à voir avec les PWA et le mode hors ligne

L'un des exemples de projets razzle implémente-t-il le support PWA (et / ou service worker)?

Peut-être moins pertinent, mais d'autres choses que j'ai placées dans le dossier public/ dans le passé lors de l'utilisation de create-react-app sont des fichiers téléchargeables liés au site Web, mais où des URL persistantes sont nécessaires. Comme avoir un document PDF auquel un lien peut être fait lors de l'envoi d'e-mails, etc. 🤷

Essayer de chercher des exemples de if / why / when webmanifests devraient être séparés du bundler:

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

Il y a un commentaire dans cet article qui renvoie à https://github.com/w3c/manifest/issues/446#issuecomment -351368893

Oui, les fichiers téléchargeables devraient y aller. Hm, mais comment ajouter ces fichiers à assets.json? Des idées? 😀 Devrions-nous faire en sorte que Webpack les trouve et les regroupe tels quels? La modification de assets.json semble hackish.

Je ne pense pas qu'il existe un exemple de PWA. Mais s'ils ont besoin d'un nom cohérent. Cela doit être géré par webpack.

Je remplacerai le plugin assets par le plugin manifest afin que nous puissions adapter la sortie.

Ajout d'un nouveau manifeste d'actifs avec tous les fichiers https://github.com/jaredpalmer/razzle/commit/1c6e9169e9d8eee256d0f118f8a88da8de85989f des suggestions d'améliorations?

A fait une version Canary maintenant :)

Je vois que le plugin manifeste n'est pas vraiment maintenu. Le mieux serait de faire le nôtre. Mais je ne connais actuellement personne d'autre que (peut-être) moi ou les gens du Webpack qui peuvent le faire.

Ajouté à la branche canari maintenant. Une sorte de hack pour le moment. Mais cela fonctionne et c'est un début qui peut être amélioré.

Après réflexion, je n'ajouterai pas cela au noyau.

Mais voici le code que j'ai trouvé:

        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;
          },
        })

Mais j'ai beaucoup appris sur les atouts;)

Désolé, je n'ai pas pu passer beaucoup de temps avec ça depuis un moment, mais ça a l'air sympa. Jetez un œil maintenant à la mise à niveau de mes affaires vers la dernière version stable de razzle et essayez votre suggestion en tant que plugin personnalisé.

Cela a l'air plutôt bien mais je suis un peu confus à ce sujet:

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

if (fileHasHash) longTermCacheFiles.add(file);

Que signifie webpackOptions.fileLoaderOutputName ? pour moi, cela semble toujours indéfini.

Seulement dans Razzle Canary

Bien, j'ai fait des progrès maintenant avec l'obtention d'une branche dans mon projet fonctionnant sur la branche canari. Cela ne fonctionne pas tout à fait, pour le moment, mes problèmes semblent principalement liés à la configuration du chargeur babel pour reconnaître les paquets frères. Je peux construire, mais j'obtiens ensuite des problèmes de «module introuvable» lorsque j'essaye de l'exécuter.

Ce n'est probablement pas trop intéressant / utile mais:

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

de mémoire, j'ai initialement emprunté la configuration à 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

et définissez NODE_PATH = .. / ou quelque chose

Peut-être devrions-nous changer certaines choses ici:

https://github.com/jaredpalmer/razzle/blob/canary/packages/razzle/config/modules.js

Ok, donc creuser un peu là-dedans, je viens de réaliser que le problème vient en fait process.env.RAZZLE_CHUNKS_MANIFEST ne plus définir

La seule chose pour laquelle je l'utilisais était de détecter les actifs pouvant être mis en cache, il semble donc que je devrais être en mesure de donner la nouvelle configuration ManifestPlugin vous avez liée maintenant pour la remplacer 🎉.

D'ACCORD!

J'ai créé un plugin personnalisé dans mon projet qui semble fonctionner suffisamment bien pour mon cas d'utilisation pour le moment. Le code que vous avez proposé a été très utile en ayant cela comme point de départ.

Je l'ai un peu changé mais pour info, je pense qu'il y a un problème avec lui où il pense que tout est traité par file-loader car cela utilise Array.prototype.every() au lieu de Array.prototype.some() : !webpackOptions.fileLoaderExclude.every(re=>re.test(file.path))

Au cas où il serait utile de partager ici:

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 peut le regarder ici https://github.com/bootleg-rust/sites/pull/2/files#diff -59ee436c0396a1f925f067b7e7cbcdee354003236a279e0a87cf8831c7f587e3

Ah oui, merci. Je suis encore en train de m'habituer aux nouveaux hooks de plugin, j'aime ça 🎉!

Je pense que le seul problème principal que j'ai toujours et que je n'ai pas pu résoudre est que, pour une raison quelconque, le plugin / chargeur scss ne fonctionne pas lorsqu'il est exécuté en mode dev en utilisant razzle start mais si je fais un razzle build complet, tout semble bien.

Des idées de ce que cela pourrait être? ou vaut-il la peine de mettre cela sur un autre problème de github quelque part?

Utilisez également modifyPaths pour les chemins personnalisés afin qu'il puisse être composé.

Ça ne marche pas comment?

Peut-être un nouveau problème .. :)

Peu importe, le chargeur sass qui ne fonctionnait pas n'était pas spécifique à Razzle. quelque chose à voir avec une incompatibilité de version ou quelque chose avec une version de react-scripts et / ou un livre d'histoires que j'avais dans un paquet frère qui soulevait deps.

Ajout de crochets pour la gestion des actifs, la fermeture 😀

Je vois que vous avez ajouté un plugin externe. J'ai encore besoin de résoudre ce problème pour le client / serveur / sans serveur. Vous avez des idées pour ça à Canary? Un peu coincé.

Les crochets que vous utilisez maintenant c'est.

Je vois que vous avez ajouté un plugin externe. J'ai encore besoin de résoudre ce problème pour le client / serveur / sans serveur. Vous avez des idées pour ça à Canary? Un peu coincé.

J'ai vraiment trouvé très pratique (principalement sur le serveur) de regrouper par défaut tous les node_modules dans le build/server.js . Pouvoir exclure entièrement le dossier node_modules de mes images de docker de production semble tout simplement super sympa.

Cela dit, je n'ai pas eu besoin d'utiliser / de tester son fonctionnement avec des dépendances natives / spécifiques à la plate-forme (j'ai le sentiment que des choses comme imagemagick auraient des problèmes)

Mon processus de réflexion général avec le plugin "externals" que j'ai créé est:

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
  ],
};

Pour être honnête avant de choisir une API de configuration "appropriée" pour cela (en particulier si cela devait être dans le noyau razzle), je devrais probablement lire la documentation du webpack pour externals plus en détail sur le différents cas d'utilisation pour les externes 😅.

Pour le moment, je ne l'utilise vraiment que pour réinitialiser les externes afin qu'ils soient vides afin que tout soit regroupé dans une application facilement portable qui ne repose pas sur node_modules au moment de l'exécution

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

krazyjakee picture krazyjakee  ·  3Commentaires

MaxGoh picture MaxGoh  ·  4Commentaires

charlie632 picture charlie632  ·  4Commentaires

corydeppen picture corydeppen  ·  3Commentaires

gabimor picture gabimor  ·  3Commentaires