Razzle: mengekspos daftar semua aset yang aman untuk menyetel `Cache-Control` yang tidak dapat diubah

Dibuat pada 31 Jul 2020  ·  40Komentar  ·  Sumber: jaredpalmer/razzle

🚀 Permintaan fitur

Saya telah menyelidiki cara terbaik untuk menambahkan middleware ke server razzle saya untuk mendeteksi kapan pun ia menyajikan file yang dibuat oleh razzle yang menyertakan webpack [hash:8] atau [contenthash:8] dalam nama file. Saya pertama kali membahas beberapa masalah yang saya hadapi di sini https://github.com/jaredpalmer/razzle/pull/1368#issuecomment -664015050

Saya ingin membuat dan mengekspos daftar file / aset yang aman untuk dianggap "tidak dapat diubah" (untuk tujuan menyetel Cache-Control header dalam tanggapan) dengan cara yang mudah digunakan tanpa transformasi ekstra dari chunks.json dan / atau file assets.json

CATATAN: saat menyetel respons kontrol-cache yang tahan lama & tidak dapat diubah, saya ingin menghindari melakukan "perkiraan" apa pun tentang apakah file dapat dianggap tidak dapat diubah (regex AKA untuk mendeteksi hash dalam nama file) karena positif palsu dapat menyebabkan file yang disimpan dalam cache untuk waktu yang lama dan tidak akan dapat diperbaiki oleh pembatalan cache sisi server, yang bisa menjadi masalah yang sangat menyakitkan untuk diatasi.

Perilaku Saat Ini

TL; DR mengapa mencoba menggunakan file json yang saat ini terbuka itu sulit:

  • Untuk mendapatkan daftar konkret dari semua file yang aman untuk disimpan dalam cache (karena mereka memiliki hash build atau konten di dalamnya) saya perlu menggunakan chunks.json dan assets.json . chunks.json menyertakan file sourcemap dan assets.json memiliki file seperti png / font dll yang tidak dimiliki chunks.json.
  • Assets.json dan chunks.json tidak dalam format yang sama (ini mungkin masalah yang terwujud bagi saya karena saya membiarkan webpack membagi hal-hal menjadi beberapa bagian) jadi memerlukan transformasi ad-hoc yang berbeda untuk menyusun daftar lengkap semua file /aktiva. Beberapa perbedaannya adalah:

    • Tampaknya untuk potongan apa pun yang tidak ada di (assets.json).client (misalnya: "client": { "js": "/static/js/bundle.6fc534aa.js" } ), assets.json mengelompokkan semua aset lain di bawah string kosong (misalnya: "": { "js": "/static/js/0.cb47cee9.chunk.js" } ).

    • jika hanya satu file yang ada dalam grup chunks.json itu akan menjadi array dengan satu item di dalamnya (misal: "client": { "css": ["filename.css"] } ), jika hanya ada satu file yang ada di assets.json itu akan menjadi hanya string tunggal (misalnya: "client": { "css": "filename.css" } ).

  • Assets.json saya saat ini berisi "json": "/../chunks.json" yang bukan sesuatu yang menurut saya seharusnya ada di sana (saya tidak yakin apakah ini bug atau bukan) tetapi saya harus menghapusnya secara manual saat membuat daftar file yang dapat diberi header respons cache-control yang berumur panjang.
  • Rencana untuk menambahkan array chunks: ["1", "2", "3"] ke chunks.json agak menjengkelkan karena itu berarti saya harus melakukan pekerjaan ekstra untuk menyaring (chunks.json).client.chunks karena tidak berisi array file seperti (chunks.json).client.css dan (chunks.json).client.js dll.
  • Sebelum perubahan yang saya buat di sini, file yang tidak ada di potongan client bahkan tidak muncul di file chunks.json . Saya membuat / menyarankan perubahan untuk mengubahnya menjadi menggunakan nomor potongan sebagai kunci karena setidaknya mereka kemudian muncul di file. Kelemahan dari ini adalah bahwa sekarang chunks.json dan assets.json berbeda lebih jauh dalam skema mereka ketika berhadapan dengan potongan yang bukan potongan bernama utama ( "client": {/* blah */ } ).

menggunakan assets.json dan chunks.json

Saat ini menggunakan assets.json dan chunks.json, inilah yang kira-kira harus saya lakukan sejauh ini

Saya belum:

  • menambahkan memuat assets.json dan menyelesaikan perbedaan antara format
  • Memfilter file / bidang di json yang saya tahu tidak dimaksudkan untuk berada di sana seperti "chunks": ["1", "2", "3"] dan "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}`,
      );
    },
  }),
);

Penjelasan yang jelas dan ringkas tentang apa perilaku / penggunaan saat ini.

Perilaku yang Diinginkan

Mungkin ada banyak cara untuk melakukan ini, tetapi hal utama yang saya inginkan hanyalah cara untuk memuat array dari semua aset yang dapat di-cache / tidak dapat diubah yang dihasilkan oleh razzle build. hasilnya bisa jadi seperti ini:

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

Solusi yang Disarankan

Saya belum sepenuhnya menyelidiki apa solusi yang baik, tetapi setelah mencoba mengumpulkan daftar "aset yang dapat disimpan dalam cache" ini pada saat runtime menggunakan assets.json dan chunks.json Saya cukup yakin bahwa di minimal cara terbaik untuk mencapai ini adalah pada waktu build dengan beberapa jenis plugin webpack dan melewati ketidakkonsistenan kedua file tersebut.

Untuk tujuan saya, saya mungkin awalnya akan mulai mencari cara untuk mencapai ini dengan plugin daripada dengan saat runtime seperti yang telah saya lakukan, tetapi saya pikir akan ada nilai yang signifikan untuk membuat ini terpesona secara default. Mampu mengatur kontrol cache berumur panjang pada file yang di-hash sebagian besar adalah alasan mengapa mereka mendapatkan hash untuk memulai, jadi mengekspos daftar semua file itu tampaknya tepat.

Siapa yang terkena dampak ini? Untuk siapa ini?

Setiap pengguna yang ingin menyetel header respons kontrol cache yang berumur panjang & tidak dapat diubah untuk file yang dihasilkan & di-hash oleh razzle.

Jelaskan alternatif yang telah Anda pertimbangkan

  • Buat daftar semua file yang tidak dapat diubah / disimpan dalam cache saat runtime dengan menggabungkan chunks.json dan assets.json (tampaknya rawan kesalahan dan rapuh).
  • Buat plugin eksternal untuk membuat daftar file yang dapat disimpan sebelumnya pada waktu pembuatan. (tampaknya mungkin rapuh di seluruh versi yang memukau untuk fitur yang sepertinya harus dipanggang / stabil)
  • Tambahkan sebagai plugin internal ke konfigurasi default yang mempesona dan tunjukkan cara untuk mengaksesnya secara default misalnya: require(process.env.RAZZLE_CACHING_MANIFEST!) . ()

Konteks tambahan

Saya akan bersedia membantu / berkontribusi untuk membuat perubahan ini tetapi saya mungkin perlu sedikit petunjuk ke arah yang benar (dan tentu saja apakah ini perubahan yang akan diterima / disambut atau tidak).

Juga pemikiran, memiliki sesuatu seperti ini mungkin membuatnya lebih mudah untuk memiliki beberapa tes / stabilitas di sekitar memastikan bahwa semuanya menggunakan [contenthash:8] daripada [hash:8] (membangun hash) jika / ketika mereka dapat https: / /github.com/jaredpalmer/razzle/issues/1331

discussion enhancement help wanted razzle webpack webpack-config

Komentar yang paling membantu

Apakah kenari melepaskan sekarang :)

Semua 40 komentar

Ini sepertinya ide yang berharga.

Itu juga terhubung ke masalah lain yaitu konfigurasi chunkGroup dalam pengoptimalan. Karena jika itu diatur, string kosong akan "dibagi", "kerangka" dll.

Jika Anda melihat next.js mereka menggunakan konfigurasi chunkGroups seperti ini.

Ketika kami mengubahnya, ini juga akan menjadi tidak kompatibel ke belakang, tetapi itu harus dilakukan. Saya memiliki beberapa perubahan besar yang juga membutuhkan rilis besar.

Tapi jangan ragu untuk menemukan beberapa kode yang memecahkan masalah ini 😀

Jika Anda melihat next.js mereka menggunakan konfigurasi chunkGroups seperti ini.

Oh keren, saya tidak tahu jika / bagaimana alat / kerangka kerja lain mendekati ini, apakah Anda memiliki tautan / contoh?

Itu juga terhubung ke masalah lain yaitu konfigurasi chunkGroup dalam pengoptimalan

Masalah terbuka yang mempesona? bisakah Anda menunjukkan yang mana sehingga saya bisa mendapatkan lebih banyak konteks 😄

Saya pikir salah satu cara potensial untuk menyelesaikan ini adalah dengan lebih menentukan bentuk / skema dari chunks.json dan assets.json . Mungkin perlu dipertimbangkan dengan hati-hati (dan memiliki versi benjolan utama) tetapi jika ada contoh bagaimana kerangka kerja lain dll telah memecahkan masalah, mungkin masuk akal untuk mengikuti arah yang sama

1361 # 1370

Dan

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

Tidak yakin tentang bagaimana mereka melakukan manifes.

Lihatlah https://github.com/jaredpalmer/razzle/issues/1377 sekarang, tambahkan contoh baru :)

@fivethreeo Saya belum bisa menghabiskan lebih banyak waktu untuk masalah ini secara khusus 😅, saya pasti akan mencoba dan meluangkan waktu untuk mencoba prarilis v4. Jika Anda pikir itu siap untuk itu, semoga saya akan berusaha untuk mencobanya dalam beberapa hari ke depan.

Saya tidak yakin apakah itu menarik, tetapi saya telah membuat apa yang saya kerjakan menjadi publik sekarang di sini .

Saya cukup tertarik untuk v4 karena mudah-mudahan berarti saya dapat menghapus sebanyak mungkin "plugin" menimpa yang harus saya atur di sini , terutama untuk skrip ketikan.

Barang aset yang dapat disimpan di cache ada di sini .

Semua file menggunakan contenthash sekarang. File yang disalin menurut saya adalah praktik yang buruk jika kita memiliki bundler.

Saya tidak yakin saya mengerti apa yang Anda maksud dengan "File yang disalin menurut saya adalah praktik yang buruk jika kita memiliki bundler".

Saat ini perilakunya adalah jika Anda meletakkan file apa pun di folder public/ tingkat atas di proyek razzle Anda.

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

Mereka diatasi ke dalam aset statis ketika Anda razzle build

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

Saya berpikir mungkin diinginkan untuk mempertahankan daftar semua aset yang disalin selama pembuatan sehingga mereka secara khusus dapat ditargetkan secara terpisah untuk menerapkan kontrol-cache ke.

Saya pikir argumen yang menentangnya (yang dapat saya pikirkan) adalah bahwa mungkin tidak ada kebutuhan bagi pengguna untuk membedakan antara file yang telah disalin oleh razzle selama pembuatan dan yang mungkin telah secara manual diletakkan di sana di luar razzle build

Saya pikir publik seharusnya hanya berisi robots.txt dan favicon.ico dan mereka tidak akan diversi oleh hashes.

Ada lagi yang harus dipaketkan oleh webpack. Semua favicon yang lebih besar harus digabungkan.

Mungkin, tetapi bahkan jika Anda ingin mempertahankan kompatibilitas "pasang dan mainkan" dengan default create-react-app , mungkin perlu dipertimbangkan bahwa manifes aplikasi dan beberapa ikon juga akan ada di sana .

Saya sangat ingat ada alasan mengapa manifest.json / manifest.webmanifest tidak boleh mengandung hash build yang merupakan salah satu alasan mengapa itu cukup sering dikecualikan dari diproses oleh bundler. Saya mungkin salah / salah mengingat tetapi mungkin ada hubungannya dengan PWA dan mode offline

Apakah salah satu proyek contoh yang mempesona menerapkan dukungan PWA (dan / atau pekerja layanan)?

Mungkin kurang relevan tetapi beberapa hal lain yang saya taruh di folder public/ di masa lalu saat menggunakan create-react-app adalah file yang dapat diunduh terkait dengan situs web tetapi di mana URL persisten diperlukan. Seperti memiliki dokumen pdf yang dapat ditautkan saat mengirim email dll 🤷

Mencoba mencari contoh if / why / when webmanifests harus dipisahkan ke bundler:

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

Ada komentar di postingan itu yang tertaut ke https://github.com/w3c/manifest/issues/446#issuecomment -351368893

Ya, file yang dapat diunduh harus ada di sana. Hm, tapi bagaimana kita menambahkan file tersebut ke assets.json? Ada ide? 😀 Haruskah kita membuat webpack menemukannya dan membundelnya sebagaimana adanya? Memodifikasi assets.json tampaknya meretas.

Saya rasa tidak ada contoh PWA. Tetapi jika mereka membutuhkan nama yang konsisten. Itu perlu ditangani oleh webpack.

Saya akan mengganti plugin assets dengan plugin manifest sehingga kita bisa menyesuaikan hasilnya.

Menambahkan aset-manifes baru dengan semua file https://github.com/jaredpalmer/razzle/commit/1c6e9169e9d8eee256d0f118f8a88da8de85989f ada saran tentang peningkatan?

Apakah kenari melepaskan sekarang :)

Saya melihat plugin nyata tidak benar-benar dipertahankan. Yang terbaik adalah melakukan sendiri. Tapi saat ini saya tidak tahu siapa pun kecuali (mungkin) saya atau orang-orang webpack yang bisa melakukannya.

Ditambahkan ke cabang kenari sekarang. Semacam peretasan untuk saat ini. Tapi itu berhasil dan merupakan awal yang bisa diperbaiki.

Setelah beberapa pertimbangan saya tidak akan menambahkan ini ke inti.

Tapi inilah kode yang saya buat:

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

Tapi saya belajar banyak tentang aset;)

Maaf saya sudah lama tidak bisa menghabiskan banyak waktu dengan ini tetapi itu terlihat rapi. Lihat sekarang untuk meningkatkan barang-barang saya ke versi stabil terbaru dan mencoba saran Anda sebagai plugin khusus.

Kelihatannya cukup bagus tapi saya agak bingung tentang ini:

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

if (fileHasHash) longTermCacheFiles.add(file);

Apa yang dimaksud dengan webpackOptions.fileLoaderOutputName ? bagi saya itu selalu tampak tidak terdefinisi.

Hanya di burung kenari yang mempesona

Baik, saya telah membuat beberapa kemajuan sekarang dengan mendapatkan cabang dalam proyek saya yang mengerjakan cabang kenari. Ini tidak cukup berfungsi, saat ini masalah saya terutama berkaitan dengan konfigurasi babel loader untuk mengenali paket saudara. Saya dapat membangun tetapi kemudian mendapatkan masalah "tidak dapat menemukan modul" ketika saya mencoba menjalankannya.

Ini mungkin tidak terlalu menarik / berguna tetapi:

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

dari memori saya awalnya meminjam konfigurasi dari 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

dan setel NODE_PATH = .. / atau sesuatu

Ok, jadi setelah menggali sedikit, saya baru menyadari bahwa masalahnya sebenarnya hanya menyebabkan process.env.RAZZLE_CHUNKS_MANIFEST tidak didefinisikan lagi 😅.

Satu-satunya hal yang saya gunakan untuk itu adalah untuk mendeteksi aset apa yang dapat disimpan dalam cache sehingga sepertinya saya dapat memberikan ManifestPlugin config baru yang Anda tautkan sekarang untuk menggantinya 🎉.

BAIK!

Saya telah membuat plugin khusus di proyek saya yang tampaknya berfungsi cukup baik untuk kasus penggunaan saya saat ini. Kode yang Anda buat sangat membantu karena itu sebagai titik awal.

Saya telah mengubahnya sedikit, tetapi FYI saya pikir ada masalah dengannya di mana ia menganggap semuanya diproses oleh file-loader karena ini menggunakan Array.prototype.every() bukan Array.prototype.some() : !webpackOptions.fileLoaderExclude.every(re=>re.test(file.path))

Jika bermanfaat untuk dibagikan di sini:

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

Atau dapat melihatnya di sini https://github.com/bootleg-rust/sites/pull/2/files#diff -59ee436c0396a1f925f067b7e7cbcdee354003236a279e0a87cf8831c7f587e3

Ah benar ya, terima kasih. Saya masih terbiasa dengan pengait plugin baru, saya menyukainya 🎉!

Saya pikir satu-satunya masalah utama yang masih saya alami yang belum dapat saya selesaikan adalah karena alasan tertentu plugin / loader scss tidak berfungsi saat berjalan dalam mode pengembang menggunakan razzle start tetapi jika saya melakukan razzle build semuanya tampak baik-baik saja.

Ada ide apa itu? atau apakah ada gunanya meletakkan ini pada masalah github yang berbeda di suatu tempat?

Gunakan juga modifikPaths untuk jalur kustom agar dapat disusun.

Tidak bekerja bagaimana?

Mungkin menjadi masalah baru .. :)

Tidak apa-apa, sass loader tidak berfungsi tidak ada yang spesifik dengan razzle. ada hubungannya dengan ketidakcocokan versi atau sesuatu dengan versi react-scripts dan / atau buku cerita yang saya miliki dalam paket saudara yang mengangkat deps.

Menambahkan pengait untuk penanganan aset, menutup 😀

Saya melihat Anda menambahkan plugin eksternal. Saya masih perlu memperbaikinya untuk klien / server / tanpa server. Punya ide untuk itu di kenari? Sedikit macet.

Kait yang Anda gunakan sekarang.

Saya melihat Anda menambahkan plugin eksternal. Saya masih perlu memperbaikinya untuk klien / server / tanpa server. Punya ide untuk itu di kenari? Sedikit macet.

Saya benar-benar merasa sangat nyaman (terutama di server) untuk default untuk menggabungkan semua node_modules ke dalam build/server.js . Mampu mengecualikan folder node_modules seluruhnya dari gambar buruh pelabuhan produksi saya sepertinya sangat bagus.

Karena itu saya belum memiliki kebutuhan untuk menggunakan / menguji cara kerjanya dengan dependensi native / platform-specific (Saya merasa hal-hal seperti imagemagick akan mengalami masalah)

Proses pemikiran umum saya dengan plugin "eksternal" yang saya buat adalah:

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

Sejujurnya sebelum menetapkan API konfigurasi yang "tepat" untuk ini (terutama jika itu akan menjadi inti yang mengasyikkan) saya mungkin harus membaca dokumen webpack untuk externals secara lebih mendalam tentang kasus penggunaan yang berbeda untuk eksternal 😅.

Saat ini saya benar-benar hanya menggunakannya untuk mengatur ulang eksternal menjadi kosong sehingga saya mendapatkan semuanya dibundel menjadi aplikasi portabel yang mudah yang tidak bergantung pada node_modules saat runtime

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

MaxGoh picture MaxGoh  ·  4Komentar

JacopKane picture JacopKane  ·  3Komentar

kkarkos picture kkarkos  ·  3Komentar

howardya picture howardya  ·  5Komentar

alexjoyner picture alexjoyner  ·  3Komentar