Razzle: كشف قائمة بجميع الأصول الآمنة لتعيين "التحكم في ذاكرة التخزين المؤقت" غير القابل للتغيير

تم إنشاؤها على ٣١ يوليو ٢٠٢٠  ·  40تعليقات  ·  مصدر: jaredpalmer/razzle

🚀 طلب ميزة

لقد كنت أبحث عن أفضل طريقة لإضافة برمجية وسيطة إلى خادم razzle الخاص بي لاكتشاف متى يقدم ملفًا تم إنشاؤه بواسطة razzle يتضمن webpack [hash:8] أو [contenthash:8] في اسم الملف. ناقشت أولاً بعض المشكلات التي أواجهها هنا https://github.com/jaredpalmer/razzle/pull/1368#issuecomment -664015050

أرغب في إنشاء وعرض قائمة الملفات / الأصول الآمنة ليتم اعتبارها "غير قابلة للتغيير" (لأغراض تعيين رؤوس Cache-Control في الردود) بطريقة يسهل استهلاكها بدون تحويل إضافي لـ chunks.json و / أو Assets.json الملفات

ملاحظة: عند إعداد استجابات للتحكم في ذاكرة التخزين المؤقت طويلة العمر وغير قابلة للتغيير ، أريد تجنب إجراء أي نوع من "التقريب" حول ما إذا كان يمكن اعتبار الملف غير قابل للتغيير (AKA regex لاكتشاف تجزئة في اسم الملف) لأن النتيجة الإيجابية الخاطئة يمكن أن تؤدي إلى ملف يتم تخزينه مؤقتًا بشكل ثابت لفترة طويلة ولن يكون قابلاً للإصلاح من خلال إبطال ذاكرة التخزين المؤقت من جانب الخادم ، والتي يمكن أن تكون مشكلة مؤلمة للغاية لحلها.

السلوك الحالي

TL ؛ DR لسبب صعوبة محاولة استخدام ملفات json المكشوفة حاليًا:

  • من أجل الحصول على القائمة الملموسة لجميع الملفات الآمنة للتخزين المؤقت بشكل ثابت (لأنها تحتوي على تجزئة بناء أو محتوى فيها) ، أحتاج إلى استخدام كل من chunks.json و assets.json . يشتمل chunks.json على ملفات sourcemap والأصول. يحتوي json على ملفات مثل png / الخطوط وما إلى ذلك والتي لا تتوفر في chunks.json.
  • لا تكون أصول json و chunks.json بنفس التنسيق (ربما تكون هذه مشكلة تظهر بالنسبة لي لأنني تركت webpack يقسم الأشياء عبر أجزاء متعددة) لذا تتطلب تحويلًا مخصصًا مختلفًا لتجميع القائمة الكاملة لجميع الملفات /أصول. بعض الاختلافات هي:

    • يبدو أنه بالنسبة إلى أي جزء ليس في (assets.json).client (على سبيل المثال: "client": { "js": "/static/js/bundle.6fc534aa.js" } ) ، تقوم الأصول بتجميع جميع الأصول الأخرى ضمن سلسلة فارغة (على سبيل المثال: "": { "js": "/static/js/0.cb47cee9.chunk.js" } ).

    • إذا كان هناك ملف واحد فقط موجود في مجموعة chunks.json ، فسيكون مصفوفة بها عنصر واحد (على سبيل المثال: "client": { "css": ["filename.css"] } ) ، إذا كان هناك ملف ملف واحد فقط موجود في الأصول .json فسيكون بدلاً من ذلك هو سلسلة واحدة (على سبيل المثال: "client": { "css": "filename.css" } ).

  • تحتوي أصولي. json حاليًا "json": "/../chunks.json" وهو شيء لا أعتقد أنه يجب أن يكون هناك (لست متأكدًا مما إذا كان هذا خطأ أم لا) ولكن يجب علي إزالة هذا يدويًا عند إعداد قائمة الملفات التي يمكن إعطاؤها رؤوس استجابة طويلة الأمد للتحكم في ذاكرة التخزين المؤقت.
  • إن خطة إضافة مصفوفة chunks: ["1", "2", "3"] إلى chunks.json مزعجة إلى حد ما لأنها تعني أن عليّ القيام بعمل إضافي لتصفية (chunks.json).client.chunks لأنه لا يحتوي على مجموعة من الملفات مثل (chunks.json).client.css و (chunks.json).client.js إلخ.
  • قبل التغيير الذي أجريته هنا ، لم تكن الملفات الموجودة في الجزء client تظهر في الملف chunks.json . لقد أجريت / اقترحت التغيير لتغييره إلى استخدام رقم (أرقام) القطعة كمفتاح لأنها على الأقل تظهر بعد ذلك في الملف. الجانب السلبي لهذا هو أن chunks.json و assets.json أكثر تنوعًا في مخططهم عند التعامل مع الأجزاء التي ليست الجزء الأساسي المسمى ( "client": {/* blah */ } ).

باستخدام الأصول. json و chunks.json

حاليًا ، باستخدام الأصول. json و chunks.json ، هذا ما كان علي فعله تقريبًا حتى الآن

ليس لدي:

  • إضافة تحميل الأصول .json حتى الآن وحل الاختلافات بين التنسيقات
  • تصفية الملفات / الحقول في json التي أعلم ليس من المفترض أن تكون هناك مثل "chunks": ["1", "2", "3"] و "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}`,
      );
    },
  }),
);

وصف واضح وموجز لما هو السلوك / الاستخدام الحالي.

السلوك المرغوب فيه

من المحتمل أن يكون هناك العديد من الطرق للقيام بذلك ولكن الشيء الأساسي الذي أريده هو مجرد وسيلة لتحميل مجموعة من جميع الأصول القابلة للتخزين المؤقت / غير القابلة للتغيير التي تم إنشاؤها بواسطة razzle build. قد تبدو النتيجة كما يلي:

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

الحل المقترح

لم أتحقق بشكل كامل من الحل الجيد ولكن بعد محاولة تجميع هذه القائمة من "الأصول القابلة للتخزين المؤقت" في وقت التشغيل باستخدام assets.json و chunks.json أنا مقتنع تمامًا أنه في الحد الأدنى أفضل طريقة لإنجاز ذلك ستكون في وقت البناء مع نوع من مكونات webpack الإضافية وتجاوز التناقضات في هذين الملفين.

من أجل أغراضي ، من المحتمل أن أبدأ في البداية في النظر في كيفية إنجاز ذلك باستخدام مكون إضافي بدلاً من وقت التشغيل كما كنت أفعل ، لكنني أعتقد أنه ستكون هناك قيمة كبيرة لامتلاك هذا المخبوزات بشكل افتراضي. إن القدرة على ضبط التحكم في ذاكرة التخزين المؤقت طويلة العمر على الملفات المجزأة هو سبب تجزئة الملفات للبدء بها ، لذا يبدو أن كشف قائمة بجميع هذه الملفات يبدو مناسبًا.

على من يؤثر هذا؟ لمن هذا؟

أي مستخدم يرغب في تعيين رؤوس استجابة مناسبة طويلة العمر وغير قابلة للتغيير في ذاكرة التخزين المؤقت للملفات التي تم إنشاؤها وتجزئتها بواسطة razzle.

صِف البدائل التي فكرت فيها

  • قم بإنشاء قائمة بجميع الملفات الثابتة / القابلة للتخزين المؤقت في وقت التشغيل عن طريق تجميع chunks.json و assets.json (يبدو أنه عرضة للخطأ وهش).
  • أنشئ مكونًا إضافيًا خارجيًا لإنشاء قائمة بالملفات القابلة للتخزين المؤقت في وقت الإنشاء مسبقًا. (ربما يبدو هشًا عبر إصدارات razzle لميزة تبدو وكأنها يجب أن تكون مخبوزة / مستقرة)
  • قم بإضافته كمكوِّن إضافي داخلي للتهيئة الافتراضية لـ razzle وكشف طريقة للوصول إليه افتراضيًا على سبيل المثال: require(process.env.RAZZLE_CACHING_MANIFEST!) . ()

سياق إضافي

سأكون على استعداد للمساعدة / المساهمة في إجراء هذا التغيير ولكن قد أحتاج إلى نقطة في الاتجاه الصحيح (وبالطبع ما إذا كان هذا تغييرًا سيتم قبوله / الترحيب به).

فكرة أيضًا ، وجود شيء كهذا قد يسهل إجراء بعض الاختبارات / الاستقرار حول ضمان أن الأشياء تستخدم [contenthash:8] بدلاً من [hash:8] (بناء التجزئة) إذا / متى يمكنهم https: / /github.com/jaredpalmer/razzle/issues/1331

discussion enhancement help wanted razzle webpack webpack-config

التعليق الأكثر فائدة

هل تم إطلاق سراح الكناري الآن :)

ال 40 كومينتر

هذه تبدو فكرة جديرة بالاهتمام

كما أنه متصل بمشكلة أخرى وهي تكوين chunkGroup في التحسين. لأنه إذا تم إعداد ذلك ، فستكون السلسلة الفارغة "مشتركة" أو "إطار عمل" وما إلى ذلك.

إذا نظرت إلى next.js ، فإنهم يستخدمون تكوين chunkGroups مثل هذا.

عندما نغير هذا سيكون الأمر غير متوافق أيضًا ، ولكن يجب القيام به. لدي بعض التغييرات الكبيرة الجارية والتي تحتاج أيضًا إلى إصدار رئيسي.

لكن لا تتردد في الخروج ببعض التعليمات البرمجية التي تحل هذا 😀

إذا نظرت إلى next.js ، فإنهم يستخدمون تكوين chunkGroups مثل هذا.

أوه رائع ، أنا لست عبر إذا / كيف تتعامل أي أدوات / أطر عمل أخرى مع هذا ، هل لديك رابط / أمثلة؟

كما أنه متصل بمشكلة أخرى وهي تكوين chunkGroup في التحسين

قضية مهرج مفتوح؟ هل يمكنك أن تدلني على أي واحد حتى أتمكن من الحصول على سياق أكثر

أعتقد أن إحدى الطرق المحتملة لحل هذه المشكلة هي تحديد الشكل / المخطط بشكل أقوى لـ chunks.json و assets.json . ربما تحتاج إلى أن يتم النظر فيها بعناية (ولديها نتوء كبير في الإصدار) ولكن إذا كانت هناك أمثلة على كيفية حل الأطر الأخرى وما إلى ذلك للمشكلة ، فقد يكون من المنطقي اتباع اتجاه مماثل

1361 # 1370

و

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

لست متأكدًا من كيفية قيامهم بالبيان.

ألق نظرة على https://github.com/jaredpalmer/razzle/issues/1377 الآن ، أضف مثالاً جديدًا :)

fivethreeo لم أتمكن من قضاء المزيد من الوقت في هذه المشكلة على وجه التحديد ، سأحاول بالتأكيد قضاء بعض الوقت في تجربة الإصدار التجريبي من الإصدار 4. إذا كنت تعتقد أنه جاهز لذلك ، آمل أن أهدف إلى تجربته خلال اليومين المقبلين.

لست متأكدًا مما إذا كان الأمر ذا أهمية كبيرة ، لكنني جعلت ما أعمل عليه علنًا الآن هنا .

أنا حريص جدًا على الإصدار 4 لأنه من المأمول أنه يعني أنه يمكنني إزالة أكبر عدد من تجاوزات "المكوّن الإضافي" التي يجب أن أقوم بتعيين الأشياء هنا ، خاصة بالنسبة للطباعة على الحروف.

عناصر الأصول القابلة للتخزين المؤقت هنا .

جميع الملفات تستخدم contenthash الآن. يمكن أن أقول إن الملفات المنسوخة هي ممارسة سيئة عندما يكون لدينا مجمع.

لست متأكدًا من فهمي لما تقصده بعبارة "الملفات المنسوخة التي يمكنني القول أنها ممارسة سيئة عندما يكون لدينا مجمع".

السلوك الحالي هو أنك إذا وضعت أي ملفات في مجلد المستوى الأعلى public/ في مشروع razzle الخاص بك.

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

يتم التعامل مع الأصول الثابتة عندما يكون لديك razzle build

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

كنت أفكر أنه قد يكون من المرغوب فيه الاحتفاظ بقائمة بجميع الأصول التي تم نسخها أثناء الإنشاء ، لذا فهي مستهدفة بشكل منفصل لتطبيق التحكم في ذاكرة التخزين المؤقت عليها.

أعتقد أن الحجة ضدها (التي يمكنني التفكير فيها) هي أنه قد لا تكون هناك حاجة للمستخدم للتمييز بين الملفات التي نسخها razzle أثناء الإنشاء وتلك التي ربما تم وضعها يدويًا هناك خارج razzle build

أعتقد أن الجمهور يجب أن يحتوي فقط على ملف robots.txt و favicon.ico ولن يتم إصداره بواسطة علامات التجزئة.

يجب تجميع أي شيء آخر بواسطة حزمة الويب. يجب تجميع أي رموز مفضلة أكبر.

ربما ، ولكن حتى إذا كنت ترغب في الحفاظ على توافق "التوصيل والتشغيل" مع create-react-app افتراضيًا ، فقد يكون من المفيد التفكير في وجود بيان التطبيق وبعض الرموز هناك أيضًا .

أتذكر جيدًا أن هناك أسبابًا لعدم احتواء manifest.json / manifest.webmanifest على تجزئة بناء وهو أحد أسباب استبعاده غالبًا من المعالجة بواسطة المجمّع. قد أكون مخطئًا / أسيء التذكر ولكن ربما يكون هناك شيء أفعله مع PWAs ووضع غير متصل بالشبكة

هل ينفذ أي من مشاريع الأمثلة الرائعة دعم PWA (و / أو عامل الخدمة)؟

ربما تكون أقل صلة بالموضوع ولكن بعض الأشياء الأخرى التي وضعتها في المجلد public/ في الماضي عند استخدام تطبيق create-react-app هي ملفات قابلة للتنزيل مرتبطة بموقع الويب ولكن تتطلب عناوين URL الدائمة. مثل وجود مستند pdf يمكن ربطه عند إرسال رسائل بريد إلكتروني وما إلى ذلك

محاولة البحث عن أمثلة لما إذا / لماذا / متى يجب فصل قوائم الويب عن أداة التجميع:

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

يوجد تعليق في هذا المنشور يرتبط بـ https://github.com/w3c/manifest/issues/446#issuecomment -351368893

نعم ، يجب أن تذهب الملفات القابلة للتنزيل إلى هناك. حسنًا ، ولكن كيف نضيف تلك الملفات إلى الأصول .json؟ أيه أفكار؟ هل يجب أن نجعل حزم الويب تجدها ونجمعها كما هي؟ يبدو تعديل الأصول. json اختراقًا.

لا أعتقد أن هناك مثال PWA. ولكن إذا كانوا بحاجة إلى اسم ثابت. هذا يحتاج إلى التعامل مع webpack.

سأستبدل المكوِّن الإضافي للأصول بمكوِّن إضافي واضح حتى نتمكن من تكييف الإخراج.

تمت إضافة بيان أصول جديد مع جميع الملفات https://github.com/jaredpalmer/razzle/commit/1c6e9169e9d8eee256d0f118f8a88da8de85989f أي اقتراحات بشأن التحسينات؟

هل تم إطلاق سراح الكناري الآن :)

أرى أنه لم يتم الحفاظ على البرنامج المساعد البيان بالفعل. الأفضل هو أن نفعل ما يخصنا. لكنني لا أعرف حاليًا أي شخص ولكن (ربما) أنا أو الأشخاص الذين يمكنهم فعل ذلك.

يضاف إلى فرع الكناري الآن. نوع من الاختراق الآن. لكنها تعمل وهي بداية يمكن تحسينها.

بعد بعض الدراسة لن أضيف هذا إلى الجوهر.

لكن هذا هو الكود الذي توصلت إليه:

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

لكنني تعلمت الكثير عن الأصول ؛)

آسف لم أتمكن من قضاء الكثير من الوقت مع هذا منذ فترة ولكن هذا يبدو أنيقًا. إلقاء نظرة الآن على ترقية أشيائي إلى أحدث إصدار مستقر وتجربة اقتراحك كمكوِّن إضافي مخصص.

تبدو جيدة جدًا ولكني مرتبك قليلاً بشأن هذا:

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

if (fileHasHash) longTermCacheFiles.add(file);

ما هو المقصود بـ webpackOptions.fileLoaderOutputName ؟ بالنسبة لي يبدو دائمًا أنه غير محدد.

فقط في رازل كناري

أنيق ، لقد أحرزت بعض التقدم الآن مع الحصول على فرع في مشروعي يعمل في فرع الكناري. إنه لا يعمل تمامًا ، في الوقت الحالي ، يبدو أن مشاكلي تتعلق بشكل أساسي بتكوين أداة تحميل babel للتعرف على حزم الأشقاء. يمكنني إنشاء مشكلات "لا يمكنني العثور على الوحدة" ولكن بعد ذلك أحصل عليها عند محاولة تشغيلها.

ربما لا يكون هذا ممتعًا / مفيدًا للغاية ولكن:

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

من الذاكرة ، اقترضت التكوين في الأصل من 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

وقم بتعيين NODE_PATH = .. / أو شيء من هذا القبيل

ربما يتعين علينا تغيير بعض الأشياء هنا:

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

حسنًا ، لقد أدركت للتو أن المشكلة تسببت بالفعل في عدم تحديد process.env.RAZZLE_CHUNKS_MANIFEST بعد الآن 😅.

الشيء الوحيد الذي كنت أستخدمه من أجله هو اكتشاف الأصول القابلة للتخزين المؤقت ، لذا يبدو أنني يجب أن أكون قادرًا على منح التكوين الجديد ManifestPlugin الذي قمت بربطه الآن لاستبداله 🎉.

نعم!

لقد أنشأت مكونًا إضافيًا مخصصًا في مشروعي يبدو أنه يعمل جيدًا بما يكفي لحالة الاستخدام الخاصة بي في الوقت الحالي. كان الكود الذي توصلت إليه مفيدًا جدًا في وجود ذلك كنقطة انطلاق.

لقد غيرتها قليلاً ولكن لمعلوماتك أعتقد أن هناك مشكلة معها حيث تعتقد أن كل شيء تتم معالجته بواسطة file-loader لأن هذا يستخدم Array.prototype.every() بدلاً من Array.prototype.some() : !webpackOptions.fileLoaderExclude.every(re=>re.test(file.path))

في حال كان من المفيد المشاركة هنا:

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

أو يمكنك الاطلاع عليها هنا https://github.com/bootleg-rust/sites/pull/2/files#diff -59ee436c0396a1f925f067b7e7cbcdee354003236a279e0a87cf8831c7f587e3

آه ، نعم ، شكرًا. ما زلت أعتاد على الإضافات الجديدة ، أحبها 🎉!

أعتقد أن المشكلة الرئيسية الوحيدة التي ما زلت أواجهها والتي لم أتمكن من حلها هي أنه لسبب ما لا يعمل المكون الإضافي / المحمل scss عند التشغيل في وضع dev باستخدام razzle start ولكن إذا قمت بعمل razzle build كاملًا ، فسيبدو كل شيء على ما يرام.

اي افكار مهما تكون؟ أم أنه يستحق وضع هذا على مشكلة جيثب مختلفة في مكان ما؟

استخدم أيضًا editPaths للمسارات المخصصة أيضًا حتى يمكن تكوينها.

لا يعمل كيف؟

قد يكون مشكلة جديدة .. :)

بغض النظر ، لم يكن محمل sass لا يعمل شيئًا محددًا مع razzle. شيء يتعلق بعدم تطابق الإصدار أو شيء ما مع إصدار react-scripts و / أو كتاب القصص الذي أمتلكه في حزمة الأخوة التي كانت تعمل على رفع الأقسام.

تمت إضافة خطافات لمعالجة الأصول وإغلاق 😀

أرى أنك أضفت مكونًا خارجيًا إضافيًا. ما زلت بحاجة لإصلاح ذلك للعميل / الخادم / الخادم. هل لديك أي أفكار لذلك في كناري؟ عالق قليلا.

الخطافات التي تستخدمها الآن هي.

أرى أنك أضفت مكونًا خارجيًا إضافيًا. ما زلت بحاجة لإصلاح ذلك للعميل / الخادم / الخادم. هل لديك أي أفكار لذلك في كناري؟ عالق قليلا.

لقد وجدت بالتأكيد أنه من الملائم للغاية (بشكل أساسي على الخادم) أن تقوم بتجميع كل node_modules في build/server.js . أن تكون قادرًا على استبعاد المجلد node_modules بالكامل من صور عامل الإنتاج الخاص بي يبدو رائعًا للغاية.

بعد أن قلت إنني لم أحتج لأي استخدام / اختبار كيفية عمله مع أي تبعيات أصلية / خاصة بالمنصة (لدي شعور بأن أشياء مثل imagemagick ستواجه مشكلات)

عملية تفكيري العامة باستخدام المكوّن الإضافي "externals" هي:

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

لأكون صادقًا قبل الاستقرار على واجهة برمجة تطبيقات التكوين "المناسبة" لهذا (خاصة إذا كانت ستصبح في نواة razzle) ، ربما يتعين علي قراءة مستندات webpack مقابل externals بتعمق أكبر قليلاً في حالات استخدام مختلفة للأطراف الخارجية 😅.

في الوقت الحالي ، أنا أستخدمه فقط لإعادة تعيين العناصر الخارجية ليكون فارغًا حتى أحصل على كل شيء مجمّعًا في تطبيق محمول بسهولة لا يعتمد على وحدات_العقد في وقت التشغيل

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

charlie632 picture charlie632  ·  4تعليقات

corydeppen picture corydeppen  ·  3تعليقات

ewolfe picture ewolfe  ·  4تعليقات

sebmor picture sebmor  ·  4تعليقات

krazyjakee picture krazyjakee  ·  3تعليقات