Razzle: рдЙрди рд╕рднреА рд╕рдВрдкрддреНрддрд┐рдпреЛрдВ рдХреА рд╕реВрдЪреА рдХреЛ рдЙрдЬрд╛рдЧрд░ рдХрд░реЗрдВ рдЬреЛ рдЕрдкрд░рд┐рд╡рд░реНрддрдиреАрдп `рдХреИрд╢-рдХрдВрдЯреНрд░реЛрд▓` рдХреЛ рд╕реЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реБрд░рдХреНрд╖рд┐рдд рд╣реИрдВ

рдХреЛ рдирд┐рд░реНрдорд┐рдд 31 рдЬреБрд▓ре░ 2020  ┬╖  40рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: jaredpalmer/razzle

ЁЯЪА рдлрд╝реАрдЪрд░ рдЕрдиреБрд░реЛрдз

рдЬрдм рднреА рдпрд╣ рдПрдХ рд╡реЗрдмрдкреИрдХ [hash:8] рдпрд╛ [contenthash:8] рдлрд╝рд╛рдЗрд▓рдирд╛рдо рд╕рд╣рд┐рдд рд╢рд╛рдорд┐рд▓ рдХрд░рддрд╛ рд╣реИ, рддреЛ рдпрд╣ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рдореИрдВ рдПрдХ рд░реЗрдЬрд╝рд▓ рджреНрд╡рд╛рд░рд╛ рдмрдирд╛рдИ рдЧрдИ рдлрд╝рд╛рдЗрд▓ рдкрд░реЛрд╕рддрд╛ рд╣реВрдБ, рдЕрдкрдиреЗ рд░реЗрдЬрд╝рд▓ рд╕рд░реНрд╡рд░ рдореЗрдВ рдорд┐рдбрд┐рд▓рд╡реЗрдпрд░ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд╕рд░реНрд╡реЛрддреНрддрдо рддрд░реАрдХреЗ рдХреА рдЬрд╛рдБрдЪ рдХрд░ рд░рд╣рд╛ рд╣реВрдБред рдореИрдВрдиреЗ рдкрд╣рд▓реА рдмрд╛рд░ рдХреБрдЫ рд╕рдорд╕реНрдпрд╛рдУрдВ рдХреА рдЪрд░реНрдЪрд╛ рдХреА рд╣реИ рдЬреЛ рдореИрдВ рдпрд╣рд╛рдБ рдкрд░ рдЪрд▓ рд░рд╣рд╛ рд╣реВрдБ https://github.com/jaredpalmer/razzle/pull/1368#issuecomment -664015050

рдореИрдВ "рдЕрдкрд░рд┐рд╡рд░реНрддрдиреАрдп" (рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рдУрдВ рдореЗрдВ Cache-Control рд╣реЗрдбрд░ рд╕реЗрдЯ рдХрд░рдиреЗ рдХреЗ рдкреНрд░рдпреЛрдЬрдиреЛрдВ рдХреЗ рд▓рд┐рдП) рдХреЗ рд░реВрдк рдореЗрдВ рд╕реБрд░рдХреНрд╖рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реБрд░рдХреНрд╖рд┐рдд рд░рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдлрд╝рд╛рдЗрд▓реЛрдВ / рд╕рдВрдкрддреНрддрд┐рдпреЛрдВ рдХреА рд╕реВрдЪреА рдХреЛ рдЙрддреНрдкрдиреНрди рдХрд░рдиреЗ рдФрд░ рдЙрдЬрд╛рдЧрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд░реВрдЭрд╛рди рдХрд░рдирд╛ рдЪрд╛рд╣реВрдВрдЧрд╛ рдЬреЛ рдЕрддрд┐рд░рд┐рдХреНрдд рдкрд░рд┐рд╡рд░реНрддрди рдХреЗ рдмрд┐рдирд╛ рдЙрдкрднреЛрдЧ рдХрд░рдирд╛ рдЖрд╕рд╛рди рд╣реИ chunks.json рдФрд░ / рдпрд╛ рдЖрд╕реНрддрд┐рдпреЛрдВ .json рдлрд╝рд╛рдЗрд▓реЗрдВ

рдзреНрдпрд╛рди рджреЗрдВ: рдЬрдм рд▓рдВрдмреЗ рд╕рдордп рддрдХ рдЬреАрд╡рд┐рдд рдФрд░ рдЕрдкрд░рд┐рд╡рд░реНрддрдиреАрдп рдХреИрд╢-рдХрдВрдЯреНрд░реЛрд▓ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рдПрдВ рд╕реЗрдЯ рдХрд░рддрд╛ рд╣реВрдВ, рддреЛ рдореИрдВ рдХрд┐рд╕реА рднреА рддрд░рд╣ рдХреЗ "рд╕рдиреНрдирд┐рдХрдЯрди" рдХреЛ рдХрд░рдиреЗ рд╕реЗ рдмрдЪрдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдХрд┐ рдХреНрдпрд╛ рдлрд╝рд╛рдЗрд▓ рдХреЛ рдЕрдкрд░рд┐рд╡рд░реНрддрдиреАрдп рдорд╛рдирд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ (рдлрд╝рд╛рдЗрд▓ рдирд╛рдо рдореЗрдВ рд╣реИрд╢ рдХрд╛ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреЗ рд▓рд┐рдП AKA regex) рдХреНрдпреЛрдВрдХрд┐ рдПрдХ рдЭреВрдареА рд╕рдХрд╛рд░рд╛рддреНрдордХ рд╣реЛ рд╕рдХрддреА рд╣реИ рдПрдХ рдлрд╝рд╛рдЗрд▓ рдХреЛ рд▓рдВрдмреЗ рд╕рдордп рд╕реЗ рдХреИрд╢ рдХрд┐рдпрд╛ рдЬрд╛ рд░рд╣рд╛ рд╣реИ рдФрд░ рд╕рд░реНрд╡рд░-рд╕рд╛рдЗрдб рдХреИрд╢ рдЕрдорд╛рдиреНрдп рд╣реЛрдиреЗ рд╕реЗ рдареАрдХ рдирд╣реАрдВ рд╣реЛрдЧрд╛, рдЬреЛ рдХрд┐ рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдмрд╣реБрдд рд╣реА рджрд░реНрджрдирд╛рдХ рд╕рдорд╕реНрдпрд╛ рд╣реЛ рд╕рдХрддреА рд╣реИред

рд╡рд░реНрддрдорд╛рди рд╡реНрдпрд╡рд╣рд╛рд░

рдЯреАрдПрд▓; рдбреАрдЖрд░ рдХреНрдпреЛрдВ рд╡рд░реНрддрдорд╛рди рдореЗрдВ рдЙрдЬрд╛рдЧрд░ json рдлрд╝рд╛рдЗрд▓реЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░ рд░рд╣рд╛ рдореБрд╢реНрдХрд┐рд▓ рд╣реИ:

  • рд╕рднреА рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреА рдареЛрд╕ рд╕реВрдЪреА рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЬреЛ рдХрд┐ рдХреИрд╢ рдХреЛ рд╕реБрд░рдХреНрд╖рд┐рдд рд░реВрдк рд╕реЗ рд╕реБрд░рдХреНрд╖рд┐рдд рд╣реИрдВ (рдХреНрдпреЛрдВрдХрд┐ рдЙрдирдХреЗ рдкрд╛рд╕ рдирд┐рд░реНрдорд╛рдг рдпрд╛ рд╕рд╛рдордЧреНрд░реА рд╣реИрд╢ рд╣реИ) рдореБрдЭреЗ рджреЛрдиреЛрдВ chunks.json рдФрд░ assets.json рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред chunks.json рдореЗрдВ рд╕реЛрд░реНрд╕рдореИрдк рдлрд╛рдЗрд▓реЗрдВ рдФрд░ рдПрд╕реЗрдЯреНрд╕ рд╢рд╛рдорд┐рд▓ рд╣реИрдВред json рдХреЗ рдкрд╛рд╕ png / рдлреЛрдВрдЯ рдЖрджрд┐ рдЬреИрд╕реА рдлрд╛рдЗрд▓реЗрдВ рд╣реИрдВ, рдЬреЛ chunks.json рдирд╣реАрдВ рдХрд░рддреА рд╣реИрдВред
  • рдПрд╕реЗрдЯреНрд╕.рдЬреЙрди рдФрд░ рдЪрдВрдХреНрд╕ред рдЬрд╕рди рдПрдХ рд╣реА рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдирд╣реАрдВ рд╣реИрдВ (рдпрд╣ рд╕рдВрднрд╡рддрдГ рдПрдХ рд╕рдорд╕реНрдпрд╛ рд╣реИ рдЬреЛ рдореЗрд░реЗ рд▓рд┐рдП рдкреНрд░рдХрдЯ рд╣реЛрддреА рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдореИрдВ рд╡реЗрдмрдкреИрдХ рдХреЛ рдХрдИ рд╡рд┐рдЦрдВрдбреВ рдореЗрдВ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд░рддрд╛ рд╣реВрдВ) рдЗрд╕рд▓рд┐рдП рд╕рднреА рдлрд╛рдЗрд▓реЛрдВ рдХреА рдкреВрд░реА рд╕реВрдЪреА рдХреЛ рдЕрд▓рдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрд▓рдЧ-рдЕрд▓рдЧ рддрджрд░реНрде рд░реВрдкрд╛рдВрддрд░рдг рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ / рд╕рдВрдкрддреНрддрд┐ред рдХреБрдЫ рдЕрдВрддрд░ рд╣реИрдВ:

    • рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ (assets.json).client (рдЬреИрд╕реЗ: "client": { "js": "/static/js/bundle.6fc534aa.js" } ) рдореЗрдВ рдХреЛрдИ рднреА рд╣рд┐рд╕реНрд╕рд╛ рдирд╣реАрдВ рд╣реИ, рдПрдХ рдЦрд╛рд▓реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреЗ рддрд╣рдд, рдЕрдиреНрдп рд╕рднреА рд╕рдВрдкрддреНрддрд┐ рд╕рдореВрд╣ (рдЬреИрд╕реЗ: "": { "js": "/static/js/0.cb47cee9.chunk.js" } )ред

    • рдЕрдЧрд░ рдХреЗрд╡рд▓ рдПрдХ рдлрд╝рд╛рдЗрд▓ chunks.json рд╕рдореВрд╣ рдореЗрдВ рдореМрдЬреВрдж рд╣реИ, рддреЛ рдЗрд╕рдореЗрдВ рдПрдХ рдЖрдЗрдЯрдо рдХреЗ рд╕рд╛рде рдПрдХ рд╕рд░рдгреА рд╣реЛрдЧреА (рдЬреИрд╕реЗ: "client": { "css": ["filename.css"] } ), рдпрджрд┐ рд╕рдВрдкрддреНрддрд┐ рдореЗрдВ рдХреЗрд╡рд▓ рдПрдХ рдлрд╝рд╛рдЗрд▓ рдлрд╝рд╛рдЗрд▓ рдореМрдЬреВрдж рд╣реИред рддреЛ рдЗрд╕рдХреЗ рдмрдЬрд╛рдп рдпрд╣ рдХреЗрд╡рд▓ рдлрд╝рд╛рдЗрд▓ рд╣реЛрдЧреА рд╕рд┐рдВрдЧрд▓ рд╕реНрдЯреНрд░рд┐рдВрдЧ (рдЬреИрд╕реЗ: "client": { "css": "filename.css" } )ред

  • рдореЗрд░реА рд╕рдВрдкрддреНрддрд┐.рдЬреЙрди рдореЗрдВ рд╡рд░реНрддрдорд╛рди рдореЗрдВ "json": "/../chunks.json" рдЬреЛ рдРрд╕рд╛ рдХреБрдЫ рдирд╣реАрдВ рд╣реИ рдЬреЛ рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рд╡рд╣рд╛рдВ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП (рдореБрдЭреЗ рдпрдХреАрди рдирд╣реАрдВ рд╣реИ рдХрд┐ рдпрд╣ рдПрдХ рдмрдЧ рд╣реИ рдпрд╛ рдирд╣реАрдВ) рд▓реЗрдХрд┐рди рдореБрдЭреЗ рдореИрдиреНрдпреБрдЕрд▓ рд░реВрдк рд╕реЗ рдЗрд╕ рд╕реВрдЪреА рдХреЛ рдмрдирд╛рддреЗ рд╕рдордп рдкрдЯреНрдЯреА рдХрд░рдирд╛ рд╣реЛрдЧрд╛ рдРрд╕реА рдлрд╛рдЗрд▓реЗрдВ рдЬрд┐рдиреНрд╣реЗрдВ рд▓рдВрдмреЗ рд╕рдордп рддрдХ рдХреИрд╢-рдХрдВрдЯреНрд░реЛрд▓ рд░рд┐рд╕реНрдкреЙрдиреНрд╕ рд╣реЗрдбрд░ рджрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред
  • Chunks.json рдХреЛ chunks: ["1", "2", "3"] рд╕рд░рдгреА рдЬреЛрдбрд╝рдиреЗ рдХреА рдпреЛрдЬрдирд╛ рдХреБрдЫ рд╣рдж рддрдХ рдХрд╖реНрдЯрдкреНрд░рдж рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рдореБрдЭреЗ (chunks.json).client.chunks рдХреЛ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрддрд┐рд░рд┐рдХреНрдд рдХрд╛рдо рдХрд░рдирд╛ рд╣реЛрдЧрд╛ рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕рдореЗрдВ рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреА рдПрдХ рд╕рд░рдгреА рдирд╣реАрдВ рд╣реИ (chunks.json).client.css рдФрд░ (chunks.json).client.js рдЖрджрд┐ред
  • рдЗрд╕ рдмрджрд▓рд╛рд╡ рд╕реЗ рдкрд╣рд▓реЗ рдореИрдВрдиреЗ рдпрд╣рд╛рдБ client рдирд╣реАрдВ рдХрдорд╛рдпрд╛ рдерд╛ chunks.json рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рднреА рджрд┐рдЦрд╛рдИ рдирд╣реАрдВ рджреЗ рд░рд╣реЗ рдереЗред рдореИрдВрдиреЗ рдЗрд╕реЗ рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реБрдЭрд╛рд╡ рджрд┐рдпрд╛ рдХрд┐ рдЗрд╕реЗ рдЪрдВрдХ рдирдВрдмрд░ (рдиреЛрдВ) рдХреЛ рдХреБрдВрдЬреА рдХреЗ рд░реВрдк рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрджрд▓ рджреЗрдВ рдХреНрдпреЛрдВрдХрд┐ рдХрдо рд╕реЗ рдХрдо рд╡реЗ рддрдм рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рджрд┐рдЦрд╛рдИ рджреЗрддреЗ рд╣реИрдВред рдЗрд╕рдХрд╛ рдирдХрд╛рд░рд╛рддреНрдордХ рдкрдХреНрд╖ рдпрд╣ рд╣реИ рдХрд┐ рдЕрдм chunks.json рдФрд░ assets.json рд╡рд┐рд╡рд┐рдз рд░реВрдк рд╕реЗ рдЙрдирдХреЗ рд╕реНрдХреАрдорд╛ рдореЗрдВ рд╣реИрдВ, рдЬреЛ рдХрд┐ рдЪрдВрдХ (рдкреНрд░рд╛рдердорд┐рдХ "client": {/* blah */ } ) рдирд╛рдо рдХреЗ рд╕рд╛рде рдирд╣реАрдВ рд╣реИрдВред

рдЖрд╕реНрддрд┐рдпреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░

рд╡рд░реНрддрдорд╛рди рдореЗрдВ рдЖрд╕реНрддрд┐рдпреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣реЗ рд╣реИрдВред Json рдФрд░ chunks.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}`,
      );
    },
  }),
);

рд╡рд░реНрддрдорд╛рди рд╡реНрдпрд╡рд╣рд╛рд░ / рдЙрдкрдпреЛрдЧ рдХреНрдпрд╛ рд╣реИ, рдЗрд╕рдХрд╛ рд╕реНрдкрд╖реНрдЯ рдФрд░ рд╕рдВрдХреНрд╖рд┐рдкреНрдд рд╡рд┐рд╡рд░рдгред

рд╡рд╛рдВрдЫрд┐рдд рд╡реНрдпрд╡рд╣рд╛рд░

рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╢рд╛рдпрдж рдХрдИ рддрд░реАрдХреЗ рд╣реЛрдВрдЧреЗ, рд▓реЗрдХрд┐рди рдкреНрд░рд╛рдердорд┐рдХ рдЪреАрдЬ рдЬреЛ рдореИрдВ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ, рд╡рд╣ рд╣реИ рдХреЗрд╡рд▓ рд░реЗрдЬрд╝рд▓ рдмрд┐рд▓реНрдб рджреНрд╡рд╛рд░рд╛ рдЙрддреНрдкрдиреНрди рд╕рднреА рдЕрдЪрд▓ / рдЕрдкрд░рд┐рд╡рд░реНрддрдиреАрдп рдкрд░рд┐рд╕рдВрдкрддреНрддрд┐рдпреЛрдВ рдХреА рдПрдХ рд╕рд░рдгреА рд▓реЛрдб рдХрд░рдирд╛ред рдкрд░рд┐рдгрд╛рдо рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦ рд╕рдХрддрд╛ рд╣реИ:

// 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 рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд░рдирдЯрд╛рдЗрдо рдкрд░ "рдХреИрдЪрд╡реЗрдмрд▓ рдПрд╕реЗрдЯреНрд╕" рдХреА рдЗрд╕ рд╕реВрдЪреА рдХреЛ рдПрдХ рд╕рд╛рде рд░рдЦрдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж рдореИрдВ рдмрд╣реБрдд рдЖрд╢реНрд╡рд╕реНрдд рд╣реВрдВ рдЗрд╕реЗ рдкреВрд░рд╛ рдХрд░рдиреЗ рдХрд╛ рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ рддрд░реАрдХрд╛ рдХреБрдЫ рдкреНрд░рдХрд╛рд░ рдХреЗ рд╡реЗрдмрдкреИрдХ рдкреНрд▓рдЧрдЗрди рдХреЗ рд╕рд╛рде рдмрд┐рд▓реНрдб-рдЯрд╛рдЗрдо рд╣реЛрдЧрд╛ рдФрд░ рдЙрди рджреЛ рдлрд╛рдЗрд▓реЛрдВ рдХреА рд╡рд┐рд╕рдВрдЧрддрд┐рдпреЛрдВ рдХреЛ рджрд░рдХрд┐рдирд╛рд░ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред

рдЕрдкрдиреЗ рдЙрджреНрджреЗрд╢реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП, рдореИрдВ рд╕рдВрднрд╡рддрдГ рд╢реБрд░реВ рдореЗрдВ рджреЗрдЦ рд░рд╣рд╛ рд╣реВрдБ рдХрд┐ рд░рдирдЯрд╛рдЗрдо рдХреЗ рдмрдЬрд╛рдп рдПрдХ рдкреНрд▓рдЧрдЗрди рдХреЗ рд╕рд╛рде рдЗрд╕реЗ рдХреИрд╕реЗ рдкреВрд░рд╛ рдХрд┐рдпрд╛ рдЬрд╛рдП рдЬреИрд╕рд╛ рдХрд┐ рдореИрдВ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ, рд▓реЗрдХрд┐рди рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЗрд╕ рдмреЗрдХ-рдЗрди рдХреЛ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рдЪрдХрд╛рдЪреМрдВрдз рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдорд╣рддреНрд╡рдкреВрд░реНрдг рдореВрд▓реНрдп рд╣реЛрдЧрд╛ред рд╣реИрд╢реЗрдб рдлрд╝рд╛рдЗрд▓реЛрдВ рдкрд░ рд▓рдВрдмреЗ рд╕рдордп рддрдХ рд░рд╣рдиреЗ рд╡рд╛рд▓реЗ рдХреИрд╢-рдирд┐рдпрдВрддреНрд░рдг рдХреЛ рд╕реЗрдЯ рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдиреЗ рдХреЗ рдХрд╛рд░рдг рдмрдбрд╝реЗ рдкреИрдорд╛рдиреЗ рдкрд░ рдЙрдиреНрд╣реЗрдВ рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣реИрд╢реЗрдб рдорд┐рд▓рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЙрди рд╕рднреА рдлрд╛рдЗрд▓реЛрдВ рдХреА рдПрдХ рд╕реВрдЪреА рдХреЛ рдЙрдЬрд╛рдЧрд░ рдХрд░рдирд╛ рдЙрдЪрд┐рдд рд▓рдЧрддрд╛ рд╣реИред

рдпрд╣ рдХреМрди рдХрд░рддрд╛ рд╣реИ? рдпреЗ рдХрд┐рд╕рдХреЗ рд▓рд┐рдП рд╣реИ?

рдХреЛрдИ рднреА рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЬреЛ рдЙрдЪрд┐рдд рд▓рдВрдмреЗ рд╕рдордп рддрдХ рдЬреАрд╡рд┐рдд рдФрд░ рдЕрдкрд░рд┐рд╡рд░реНрддрдиреАрдп рдХреИрд╢-рдХрдВрдЯреНрд░реЛрд▓ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рд╣реЗрдбрд░ рдЙрддреНрдкрдиреНрди рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рдЬреЛ рдХрд┐ рд░реЗрдЬрд╝рд▓ рджреНрд╡рд╛рд░рд╛ рдЙрддреНрдкрдиреНрди рдФрд░ рд╣реИрд╢реЗрдб рдХреЗ рд▓рд┐рдП рд╣реИред

рдЖрдкрдХреЗ рджреНрд╡рд╛рд░рд╛ рдЪреБрдиреЗ рдЧрдП рд╡рд┐рдХрд▓реНрдкреЛрдВ рдХрд╛ рд╡рд░реНрдгрди рдХрд░реЗрдВ

  • рдПрдХ рд╕рд╛рде chunks.json рдФрд░ assets.json (рд▓рдЧрддрд╛ рд╣реИ рддреНрд░реБрдЯрд┐ рдкреНрд░рд╡рдг рдФрд░ рдирд╛рдЬреБрдХ рд▓рдЧрддрд╛ рд╣реИ) clobbering рджреНрд╡рд╛рд░рд╛ рдХреНрд░рдо рдореЗрдВ рд╕рднреА рдЕрдкрд░рд┐рд╡рд░реНрддрдиреАрдп / cacheable рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреА рд╕реВрдЪреА рдмрдирд╛рдПрдБред
  • рдмрд┐рд▓реНрдбрдЯрд╛рдЗрдо рдореЗрдВ рдХреИрд╢реЗрдмрд▓ рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреА рд╕реВрдЪреА рддреИрдпрд╛рд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдмрд╛рд╣рд░реА рдкреНрд▓рдЧрдЗрди рдмрдирд╛рдПрдВред (рд╕рдВрднрд╡рддрдГ рдПрдХ рд╡рд┐рд╢реЗрд╖рддрд╛ рдХреЗ рд▓рд┐рдП рдЪрдордХрджрд╛рд░ рд╕рдВрд╕реНрдХрд░рдгреЛрдВ рдореЗрдВ рдирд╛рдЬреБрдХ рд▓рдЧрддрд╛ рд╣реИ рдЬреЛ рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЗрд╕реЗ рдмреЗрдХ рдХрд┐рдпрд╛ рд╣реБрдЖ / рд╕реНрдерд┐рд░ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП)
  • рдЗрд╕реЗ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдХреЙрдиреНрдлрд┐рдЧрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЖрдВрддрд░рд┐рдХ рдкреНрд▓рдЧрдЗрди рдХреЗ рд░реВрдк рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ рдФрд░ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рдЗрд╕реЗ рдПрдХреНрд╕реЗрд╕ рдХрд░рдиреЗ рдХрд╛ рдПрдХ рддрд░реАрдХрд╛ рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП: 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 рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рд╣реИ

рдПрдХ рдЦреБрд▓реА рдЪрдХрд╛рдЪреМрдВрдз рдореБрджреНрджрд╛? рдХреНрдпрд╛ рдЖрдк рдореБрдЭреЗ рдмрддрд╛ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдореИрдВ рдХрд┐рд╕рд╕реЗ рдЕрдзрд┐рдХ рд╕рдВрджрд░реНрдн рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ which

рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЗрд╕реЗ рд╕реБрд▓рдЭрд╛рдиреЗ рдХрд╛ рд╕рдВрднрд╛рд╡рд┐рдд рдПрдХ рддрд░реАрдХрд╛ рдореМрдЬреВрджрд╛ 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 рдореИрдВ рдЗрд╕ рдореБрджреНрджреЗ рдкрд░ рдХрд┐рд╕реА рднреА рдЕрдзрд┐рдХ рд╕рдордп рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ

рдореБрдЭреЗ рдпрдХреАрди рдирд╣реАрдВ рд╣реИ рдХрд┐ рдЕрдЧрд░ рдпрд╣ рдмрд╣реБрдд рдЬреНрдпрд╛рджрд╛ рджрд┐рд▓рдЪрд╕реНрдкреА рд╡рд╛рд▓рд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдореИрдВрдиреЗ рдпрд╣рд╛рдВ рд╕рд╛рд░реНрд╡рдЬрдирд┐рдХ рд░реВрдк рд╕реЗ рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рд╣реВрдВред

рдореИрдВ v4 рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд рдЙрддреНрд╕реБрдХ рд╣реВрдВ рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рдореИрдВ "рдкреНрд▓рдЧрдЗрди" рдХреЗ рдХрдИ рдХреЛ рд╣рдЯрд╛ рд╕рдХрддрд╛ рд╣реВрдВ рдУрд╡рд░рд░рд╛рдЗрдбреНрд╕ рдореБрдЭреЗ рдпрд╣рд╛рдВ рдЪреАрдЬреЛрдВ рдХреЛ рд╕реЗрдЯ рдХрд░рдирд╛ рд╣реЛрдЧрд╛, рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХреЗ рд▓рд┐рдПред

рдХреИрдЪ рдХрд░рдиреЗ рдпреЛрдЧреНрдп рд╕рдВрдкрддреНрддрд┐ рд╕рд╛рдорд╛рди рдпрд╣рд╛рдБ рд╣реИ ред

рд╕рднреА рдлрд╛рдЗрд▓реЗрдВ рдЕрдм рд╕рд╛рдордЧреНрд░реА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреА рд╣реИрдВред рдирдХрд▓ рдХреА рдЧрдИ рдлрд╛рдЗрд▓реЗрдВ рдореИрдВ рдХрд╣реВрдВрдЧрд╛ рдХрд┐ рдЬрдм рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдмрдВрдбрд▓рд░ рд╣реЛрддрд╛ рд╣реИ рддреЛ рдпрд╣ рдПрдХ рдмреБрд░рд╛ рдЕрднреНрдпрд╛рд╕ рд╣реИред

рдореБрдЭреЗ рдпрдХреАрди рдирд╣реАрдВ рд╣реИ рдХрд┐ рдореБрдЭреЗ рд╕рдордЭ рдореЗрдВ рдЖрдпрд╛ рд╣реИ рдХрд┐ "рдирдХрд▓ рдХреА рдЧрдИ рдлрд╝рд╛рдЗрд▓реЛрдВ рд╕реЗ рдореЗрд░рд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рдЬрдм рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдПрдХ рдмрдВрдбрд▓рд░ рд╣реЛрддрд╛ рд╣реИ рддреЛ рдпрд╣ рдПрдХ рдмреБрд░рд╛ рдЕрднреНрдпрд╛рд╕ рд╣реИ"ред

рд╡рд░реНрддрдорд╛рди рдореЗрдВ рд╡реНрдпрд╡рд╣рд╛рд░ рдпрд╣ рд╣реИ рдХрд┐ рдпрджрд┐ рдЖрдк рдХрд┐рд╕реА рднреА рдлрд╛рдЗрд▓ рдХреЛ рдЕрдкрдиреА рдЪрдХрд╛рдЪреМрдВрдз рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ public/ рдлрд╝реЛрд▓реНрдбрд░ рдореЗрдВ рд░рдЦрддреЗ рд╣реИрдВред

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 build рдмрд╛рд╣рд░ рд╡рд╣рд╛рдБ рд░рдЦрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред

рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рд╕рд╛рд░реНрд╡рдЬрдирд┐рдХ рдореЗрдВ рдХреЗрд╡рд▓ robots.txt рдФрд░ favicon.ico рд╢рд╛рдорд┐рд▓ рд╣реЛрдиреЗ рдЪрд╛рд╣рд┐рдП рдФрд░ рд╡реЗ рд╣реИрд╢ рджреНрд╡рд╛рд░рд╛ рд╕рдВрд╕реНрдХрд░рдгрд┐рдд рдирд╣реАрдВ рдХрд┐рдП рдЬрд╛рдПрдВрдЧреЗред

рдХрд┐рд╕реА рднреА рдЪреАрдЬрд╝ рдХреЛ рд╡реЗрдмрдкреИрдХ рджреНрд╡рд╛рд░рд╛ рдмрдВрдбрд▓ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рдХрд┐рд╕реА рднреА рдмрдбрд╝реЗ рдлреЗрд╡рд┐рдХреЙрди рдХреЛ рдмрдВрдбрд▓ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред

рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣рд╛рдВ рддрдХ тАЛтАЛрдХрд┐ рдЕрдЧрд░ рдЖрдк рдПрдХ рдбрд┐рдлрд╝реЙрд▓реНрдЯ create-react-app рдХреЗ рд╕рд╛рде "рдкреНрд▓рдЧ рдПрдВрдб рдкреНрд▓реЗ" рд╕рдВрдЧрддрддрд╛ рдмрдирд╛рдП рд░рдЦрдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рддреЛ рдпрд╣ рд╡рд┐рдЪрд╛рд░ рдХрд░рдиреЗ рдпреЛрдЧреНрдп рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдХрд┐ рдРрдк рдкреНрд░рдХрдЯ рдФрд░ рдХреБрдЫ рдЖрдЗрдХрди рднреА рд╡рд╣рд╛рдВ рдореМрдЬреВрдж рд╣реЛрдВрдЧреЗред

рдореБрдЭреЗ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдпрд╛рдж рд╣реИ рдХрд┐ рдРрд╕реЗ рдХрд╛рд░рдг рд╣реИрдВ рдЬрд┐рдирдХреА рд╡рдЬрд╣ рд╕реЗ manifest.json / manifest.webmanifest рдореЗрдВ рдПрдХ рдмрд┐рд▓реНрдб рд╣реИрд╢ рдирд╣реАрдВ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП, рдЬреЛ рдПрдХ рдХрд╛рд░рдг рд╣реИ рдХрд┐ рдЗрд╕реЗ рдЕрдХреНрд╕рд░ рдмрдВрдбрд▓рд░ рджреНрд╡рд╛рд░рд╛ рд╕рдВрд╕рд╛рдзрд┐рдд рд╣реЛрдиреЗ рд╕реЗ рдмрд╛рд╣рд░ рд░рдЦрд╛ рдЬрд╛рддрд╛ рд╣реИред рдореИрдВ рдЧрд▓рдд / рдЧрд▓рдд рдпрд╛рдж рд░рдЦ рд╕рдХрддрд╛ рд╣реВрдВ, рд▓реЗрдХрд┐рди рд╕рдВрднрд╡рддрдГ PWA рдФрд░ рдСрдлрд╝рд▓рд╛рдЗрди рдореЛрдб рдХреЗ рд╕рд╛рде рдХреБрдЫ рдХрд░рдирд╛ рд╣реИ

рдХреНрдпрд╛ рдХреЛрдИ рднреА рдЙрджрд╛рд╣рд░рдг рдЙрджрд╛рд╣рд░рдг рдкрд░рд┐рдпреЛрдЬрдирд╛рдПрдВ PWA (рдФрд░ / рдпрд╛ рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛) рдХреЗ рд╕рдорд░реНрдерди рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддреА рд╣реИрдВ?

рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдХрд┐ рдХрдо рдкреНрд░рд╛рд╕рдВрдЧрд┐рдХ рд╣реЛ рд▓реЗрдХрд┐рди рдХреБрдЫ рдЕрдиреНрдп рдЪреАрдЬреЗрдВ рдЬреЛ рдореИрдВрдиреЗ public/ рдлреЛрд▓реНрдбрд░ рдореЗрдВ рд░рдЦреА рд╣реИрдВ рдЬрдм рдХреНрд░рд┐рдПрдЯ-рд░рд┐рдПрдХреНрд╢рди-рдРрдк рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП рд╡реЗрдмрд╕рд╛рдЗрдЯ рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рдиреЗ рдпреЛрдЧреНрдп рдлрд╛рдЗрд▓реЗрдВ рд╣реИрдВ рд▓реЗрдХрд┐рди рдЬрд╣рд╛рдВ рд▓рдЧрд╛рддрд╛рд░ URL рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдЬреИрд╕реЗ рдкреАрдбреАрдПрдл рдбреЙрдХреНрдпреВрдореЗрдВрдЯ рдЬреЛ рдИрдореЗрд▓ рдЖрджрд┐ рднреЗрдЬрддреЗ рд╕рдордп рд▓рд┐рдВрдХ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ

рдЕрдЧрд░ / рдХреНрдпреЛрдВ / рдЬрдм рд╡реЗрдмрдореЗрдирдлреЗрд╕реНрдЯреНрд╕ рдХреЛ рдмрдВрдбрд▓рд░ рдХреЗ рд▓рд┐рдП рдЕрд▓рдЧ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП рджреЗрдЦрдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░ рд░рд╣рд╛ рд╣реИ:

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 рд╕реЗ рд▓рд┐рдВрдХ рдХрд░рддреА рд╣реИ

рд╣рд╛рдВ, рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рдиреЗ рдпреЛрдЧреНрдп рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреЛ рд╡рд╣рд╛рдВ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рд╣рдореНрдо, рд▓реЗрдХрд┐рди рд╣рдо рдЙрди рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреЛ рд╕рдВрдкрддреНрддрд┐ рдореЗрдВ рдХреИрд╕реЗ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВред рдХреЛрдИ рд╡рд┐рдЪрд╛рд░? BundShould рд╣рдо рд╡реЗрдмрдкреИрдХ рдЙрдиреНрд╣реЗрдВ рдвреВрдВрдврддреЗ рд╣реИрдВ рдФрд░ рдЬреИрд╕рд╛ рдХрд┐ рдмрдВрдбрд▓ рд╣реИ? рд╕рдВрдкрддреНрддрд┐ рдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░рдирд╛ред Json рд╣реИрдХрд┐рд╢ рд▓рдЧрддрд╛ рд╣реИред

рдореБрдЭреЗ рдирд╣реАрдВ рд▓рдЧрддрд╛ рдХрд┐ PWA рдХрд╛ рдХреЛрдИ рдЙрджрд╛рд╣рд░рдг рд╣реИред рд▓реЗрдХрд┐рди рдЕрдЧрд░ рдЙрдиреНрд╣реЗрдВ рдПрдХ рд╕реБрд╕рдВрдЧрдд рдирд╛рдо рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдЬрд┐рд╕реЗ рд╡реЗрдмрдкреИрдХ рджреНрд╡рд╛рд░рд╛ рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред

рдореИрдВ рдПрд╕реЗрдЯ рдкреНрд▓рдЧрдЗрди рдХреЛ рдореИрдирд┐рдлрд╝реЗрд╕реНрдЯ рдкреНрд▓рдЧрдЗрди рд╕реЗ рдмрджрд▓реВрдВрдЧрд╛ рддрд╛рдХрд┐ рд╣рдо рдЖрдЙрдЯрдкреБрдЯ рдХреЛ рдЕрдиреБрдХреВрд▓рд┐рдд рдХрд░ рд╕рдХреЗрдВред

рд╕рднреА рдлрд╛рдЗрд▓реЛрдВ рдХреЗ рд╕рд╛рде рдПрдХ рдирдпрд╛ рдПрд╕реЗрдЯ-рдореИрдирд┐рдлрд╝реЗрд╕реНрдЯ рдЬреЛрдбрд╝рд╛ рдЧрдпрд╛ рд╣реИ https://github.com/jaredpalmer/razzle/commit/1c6e9169e9d8eee256d0f118f8a88da8de859894 рд╕реБрдзрд╛рд░реЛрдВ рдкрд░ рдХреЛрдИ рд╕реБрдЭрд╛рд╡?

рдЕрдм рдПрдХ рдХреИрдирд░реА рд░рд┐рд▓реАрдЬ :)

рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдкреНрд░рдХрдЯ рдкреНрд▓рдЧрдЗрди рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдмрдирд╛рдП рдирд╣реАрдВ рд░рдЦрд╛ рдЧрдпрд╛ рд╣реИред рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ рддреЛ рдпрд╣реА рд╣реЛрдЧрд╛ рдХрд┐ рд╣рдо рдЕрдкрдирд╛ рдХрд╛рдо рдХрд░реЗрдВред рд▓реЗрдХрд┐рди рдореИрдВ рд╡рд░реНрддрдорд╛рди рдореЗрдВ рдХрд┐рд╕реА рдХреЛ рдирд╣реАрдВ рдЬрд╛рдирддрд╛ рд▓реЗрдХрд┐рди (рд╢рд╛рдпрдж) рдореБрдЭреЗ рдпрд╛ рд╡реЗрдмрдкреИрдХ рд▓реЛрдЧреЛрдВ рдХреЛ рдЬреЛ рдРрд╕рд╛ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

рдЕрдм рдХреИрдирд░реА рд╢рд╛рдЦрд╛ рдореЗрдВ рдЬреЛрдбрд╝рд╛ рдЧрдпрд╛ред рдЕрдм рдХреЗ рд▓рд┐рдП рдПрдХ рд╣реИрдХ рдХреА рддрд░рд╣ред рд▓реЗрдХрд┐рди рдпрд╣ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ рдФрд░ рдПрдХ рд╢реБрд░реБрдЖрдд рд╣реИ рдЬрд┐рд╕реЗ рдмреЗрд╣рддрд░ рдмрдирд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред

рдХреБрдЫ рд╡рд┐рдЪрд╛рд░ рдХреЗ рдмрд╛рдж рдореИрдВ рдЗрд╕реЗ рдХреЛрд░ рдореЗрдВ рдирд╣реАрдВ рдЬреЛрдбрд╝реВрдВрдЧрд╛ред

рд▓реЗрдХрд┐рди рдпрд╣рд╛рдБ рдореИрдВ рдХреЗ рд╕рд╛рде рдЖрдпрд╛ рдХреЛрдб рд╣реИ:

        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 рдЕрд░реНрде рдХреНрдпрд╛ рд╣реИ? рдореЗрд░реЗ рд▓рд┐рдП рдпрд╣ рд╣рдореЗрд╢рд╛ рдЕрдкрд░рд┐рднрд╛рд╖рд┐рдд рд▓рдЧрддрд╛ рд╣реИред

рдХреЗрд╡рд▓ рдЪрдХрд╛рдЪреМрдВрдз рдХреИрдирд░реА рдореЗрдВ

рдиреАрдЯ, рдореИрдВрдиреЗ рдЕрдкрдиреА рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ рдПрдХ рд╢рд╛рдЦрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд╕рд╛рде рдЕрдм рдХреБрдЫ рдкреНрд░рдЧрддрд┐ рдХреА рд╣реИ рдЬреЛ рдХреИрдирд░реА рд╢рд╛рдЦрд╛ рдкрд░ рдХрд╛рдо рдХрд░ рд░рд╣реА рд╣реИред рдпрд╣ рдХрд╛рдлреА рдХрд╛рдо рдирд╣реАрдВ рдХрд░ рд░рд╣рд╛ рд╣реИ, рдлрд┐рд▓рд╣рд╛рд▓ рдореЗрд░реЗ рдореБрджреНрджреЗ рдореБрдЦреНрдп рд░реВрдк рд╕реЗ рд╕рд┐рдмрд▓рд┐рдВрдЧ рдкреИрдХреЗрдЬ рдХреЛ рдкрд╣рдЪрд╛рдирдиреЗ рдХреЗ рд▓рд┐рдП рдмреИрдмрд▓ рд▓реЛрдбрд░ рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рдиреЗ рдХреЗ рд╕рд╛рде рд▓рдЧрддреЗ рд╣реИрдВред рдЬрдм рдореИрдВ рдЗрд╕реЗ рдЪрд▓рд╛рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ рддреЛ рдореИрдВ рдирд┐рд░реНрдорд╛рдг рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ рд▓реЗрдХрд┐рди "рдореЙрдбреНрдпреВрд▓ рдирд╣реАрдВ рдвреВрдВрдв рд╕рдХрддрд╛"ред

рдпрд╣ рд╢рд╛рдпрдж рдмрд╣реБрдд рджрд┐рд▓рдЪрд╕реНрдк / рдЙрдкрдпреЛрдЧреА рдирд╣реАрдВ рд╣реИ рд▓реЗрдХрд┐рди:

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 рд╣реЛрдиреЗ рдХреЗ рдХрд╛рд░рдг рдкрд░рд┐рднрд╛рд╖рд┐рдд рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ itред

рдХреЗрд╡рд▓ рдПрдХ рдЪреАрдЬ рдЬреЛ рдореИрдВ рдЗрд╕рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рдерд╛, рдпрд╣ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдерд╛ рдХрд┐ рдХреМрди рд╕реА рд╕рдВрдкрддреНрддрд┐рдпрд╛рдВ рдЙрдкрд▓рдмреНрдз рдирд╣реАрдВ рдереАрдВ, рдЗрд╕рд▓рд┐рдП рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдореБрдЭреЗ рдирдпрд╛ ManifestPlugin рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рди рджреЗрдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП рдЬрд┐рд╕реЗ рдЖрдкрдиреЗ рдЗрд╕реЗ рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдм a рд╕реЗ рдмрджрд▓ рджрд┐рдпрд╛ рд╣реИред

рдареАрдХ рд╣реИ!

рдореИрдВрдиреЗ рдЕрдкрдиреА рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ рдПрдХ рдХрд╕реНрдЯрдо рдкреНрд▓рдЧрдЗрди рдмрдирд╛рдпрд╛ рд╣реИ рдЬреЛ рдлрд┐рд▓рд╣рд╛рд▓ рдореЗрд░реЗ рдЙрдкрдпреЛрдЧ рдХреЗ рдорд╛рдорд▓реЗ рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рд░реВрдк рд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред рдЖрдк рдЬрд┐рд╕ рдХреЛрдб рдХреЗ рд╕рд╛рде рдЖрдП рдереЗ, рд╡рд╣ рд╢реБрд░реБрдЖрддреА рдмрд┐рдВрджреБ рдХреЗ рд░реВрдк рдореЗрдВ рдмрд╣реБрдд рдорджрджрдЧрд╛рд░ рдерд╛ред

рдореИрдВрдиреЗ рдЗрд╕реЗ рдХрд╛рдлреА рд╣рдж рддрдХ рдмрджрд▓ рджрд┐рдпрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ FYI рдХрд░реЗрдВ, рдЗрд╕рдХреЗ рд╕рд╛рде рдПрдХ рдРрд╕рд╛ рдореБрджреНрджрд╛ рд╣реИ рдЬрд╣рд╛рдБ рдпрд╣ рд╕реЛрдЪрддрд╛ рд╣реИ рдХрд┐ рд╕рдм рдХреБрдЫ file-loader рд╕реЗ рд╕рдВрд╕рд╛рдзрд┐рдд рд╣реЛрддрд╛ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ Array.prototype.some() : !webpackOptions.fileLoaderExclude.every(re=>re.test(file.path)) рдмрдЬрд╛рдп Array.prototype.every() рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ !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

рдЖрд╣ рдареАрдХ рд╣реИ рд╣рд╛рдБ, рдзрдиреНрдпрд╡рд╛рджред рдореБрдЭреЗ рдЕрднреА рднреА рдирдП рдкреНрд▓рдЧрдЗрди рд╣реБрдХ рдХреА рдЖрджрдд рд╣реИ, рдореБрдЭреЗ рдпрд╣ рдкрд╕рдВрдж рд╣реИ the!

рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдПрдХрдорд╛рддреНрд░ рдореБрдЦреНрдп рд╕рдорд╕реНрдпрд╛ рдореБрдЭреЗ рдЕрднреА рднреА рд╣реЛ рд░рд╣реА рд╣реИ рдЬрд┐рд╕реЗ рдореИрдВ рд╣рд▓ рдирд╣реАрдВ рдХрд░ рдкрд╛ рд░рд╣рд╛ рд╣реВрдВ рдХрд┐ рдХрд┐рд╕реА рдХрд╛рд░рдг рд╕реЗ scss рдкреНрд▓рдЧрдЗрди / рд▓реЛрдбрд░ razzle start рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рджреЗрд╡ рдореЛрдб рдореЗрдВ рдЪрд▓рдиреЗ рдкрд░ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ razzle build рдХрд░реВрдВ рддреЛ рдпрд╣ рд╕рдм рдареАрдХ рд▓рдЧрддрд╛ рд╣реИред

рдХрд┐рд╕реА рднреА рд╡рд┐рдЪрд╛рд░ рдпрд╣ рдХреНрдпрд╛ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ? рдпрд╛ рдпрд╣ рдПрдХ рдЕрд▓рдЧ github рдореБрджреНрджреЗ рдкрд░ рдХрд╣реАрдВ рд░рдЦрдиреЗ рдХреЗ рд▓рд╛рдпрдХ рд╣реИ?

рдХрд╕реНрдЯрдо рдкрде рдХреЗ рд▓рд┐рдП рднреА рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░реЗрдВ рдкреИрда рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ рддрд╛рдХрд┐ рдЗрд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛ рд╕рдХреЗред

рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ рдХреИрд╕реЗ?

рдПрдХ рдирдпрд╛ рдореБрджреНрджрд╛ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ .. :)

рдХреЛрдИ рдмрд╛рдд рдирд╣реАрдВ, sass рд▓реЛрдбрд░ рдХрд╛рдо рдирд╣реАрдВ рдХрд░ рд░рд╣рд╛ рдерд╛ рдЪрдХрд╛рдЪреМрдВрдз рдХреЗ рд╕рд╛рде рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХреБрдЫ рднреА рдирд╣реАрдВ рдерд╛ред рдПрдХ рд╕рдВрд╕реНрдХрд░рдг рдмреЗрдореЗрд▓ рдХреЗ рд╕рд╛рде рдХреБрдЫ рдХрд░рдирд╛ рд╣реИ рдпрд╛ react-scripts рдФрд░ / рдпрд╛ рд╕реНрдЯреЛрд░реАрдмреБрдХ рдХреЗ рдПрдХ рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд╕рд╛рде рдХреБрдЫ рд╣реИ рдЬреЛ рдореЗрд░реЗ рдкрд╛рд╕ рдПрдХ рднрд╛рдИ рдкреИрдХреЗрдЬ рдореЗрдВ рдерд╛ рдЬреЛ рдбрд┐рдкреНрд╕ рд▓рд╣рд░рд╛ рд░рд╣рд╛ рдерд╛ред

рдПрд╕реЗрдЯ рд╣реИрдВрдбрд▓рд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдЬреЛрдбрд╝реЗ рдЧрдП рд╣реБрдХ, рд╕рдорд╛рдкрди handling

рдореИрдВ рджреЗрдЦ рд░рд╣рд╛ рд╣реВрдВ рдХрд┐ рдЖрдкрдиреЗ рдПрдХ рдПрдХреНрд╕рдЯрд░реНрдирд▓ рдкреНрд▓рдЧрдЗрди рдЬреЛрдбрд╝рд╛ рд╣реИред рдореБрдЭреЗ рдЕрднреА рднреА рдХреНрд▓рд╛рдЗрдВрдЯ / рд╕рд░реНрд╡рд░ / рд╕рд░реНрд╡рд░ рд░рд╣рд┐рдд рдХреЗ рд▓рд┐рдП рдЗрд╕реЗ рдареАрдХ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдХреИрдирд░реА рдореЗрдВ рдЙрд╕ рдХреЗ рд▓рд┐рдП рдХреЛрдИ рд╡рд┐рдЪрд╛рд░ рд╣реИ? рдереЛрдбрд╝рд╛ рдЕрдЯрдХ рдЧрдпрд╛ред

рдЖрдкрдХреЗ рджреНрд╡рд╛рд░рд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рд╣реБрдХ рдЕрдм рд╣реИрдВред

рдореИрдВ рджреЗрдЦ рд░рд╣рд╛ рд╣реВрдВ рдХрд┐ рдЖрдкрдиреЗ рдПрдХ рдПрдХреНрд╕рдЯрд░реНрдирд▓ рдкреНрд▓рдЧрдЗрди рдЬреЛрдбрд╝рд╛ рд╣реИред рдореБрдЭреЗ рдЕрднреА рднреА рдХреНрд▓рд╛рдЗрдВрдЯ / рд╕рд░реНрд╡рд░ / рд╕рд░реНрд╡рд░ рд░рд╣рд┐рдд рдХреЗ рд▓рд┐рдП рдЗрд╕реЗ рдареАрдХ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдХреИрдирд░реА рдореЗрдВ рдЙрд╕ рдХреЗ рд▓рд┐рдП рдХреЛрдИ рд╡рд┐рдЪрд╛рд░ рд╣реИ? рдереЛрдбрд╝рд╛ рдЕрдЯрдХ рдЧрдпрд╛ред

рдореБрдЭреЗ рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рдпрд╣ рд╕реБрдкрд░ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ (рдореБрдЦреНрдп рд░реВрдк рд╕реЗ рд╕рд░реНрд╡рд░ рдкрд░) рд╕рднреА node_modules рдХреЛ build/server.js рдмрдВрдбрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рдорд┐рд▓рд╛ рд╣реИред рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдореЗрд░реЗ рдЙрддреНрдкрд╛рджрди docker рдЫрд╡рд┐рдпреЛрдВ рд╕реЗ рдкреВрд░реА рддрд░рд╣ рд╕реЗ node_modules рдлрд╝реЛрд▓реНрдбрд░ рдХреЛ рдмрд╛рд╣рд░ рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдиреЗ рдХреЗ рдирд╛рддреЗ рд╕рд┐рд░реНрдл рд╕реБрдкрд░ рдЕрдЪреНрдЫрд╛ рд▓рдЧрддрд╛ рд╣реИред

рдпрд╣ рдХрд╣рдиреЗ рдХреЗ рдмрд╛рдж рдХрд┐ рдореБрдЭреЗ рдХрд┐рд╕реА рднреА рджреЗрд╢реА / рдкреНрд▓реЗрдЯрдлрд╝реЙрд░реНрдо-рд╡рд┐рд╢рд┐рд╖реНрдЯ рдирд┐рд░реНрднрд░рддрд╛ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ / рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреА рдХреЛрдИ рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ (рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЗрдореЗрдЬрдореИрдЧрд┐рдХ рдЬреИрд╕реА рд╕рдорд╕реНрдпрд╛рдПрдВ рд╣реЛрдВрдЧреА)

рдореЗрд░реЗ рджреНрд╡рд╛рд░рд╛ рдХрд┐рдП рдЧрдП "рдПрдХреНрд╕рдЯрд░реНрдирд▓" рдкреНрд▓рдЧрдЗрди рдХреЗ рд╕рд╛рде рдореЗрд░реА рд╕рд╛рдорд╛рдиреНрдп рд╡рд┐рдЪрд╛рд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╣реИ:

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

рдЗрд╕рдХреЗ рд▓рд┐рдП "рдЙрдЪрд┐рдд" рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдПрдкреАрдЖрдИ рдкрд░ рдмрд╕рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рдИрдорд╛рдирджрд╛рд░ рд╣реЛрдиреЗ рдХреЗ рд▓рд┐рдП (рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ рдпрджрд┐ рдпрд╣ рдЪрдордХрджрд╛рд░ рдХреЛрд░ рдореЗрдВ рд╣реЛрдиреЗ рдЬрд╛ рд░рд╣рд╛ рдерд╛) рддреЛ рдореБрдЭреЗ рд╢рд╛рдпрдж externals рд▓рд┐рдП рд╡реЗрдмрдкреИрдХ рдбреЙрдХреНрд╕ рдХреЛ рдереЛрдбрд╝рд╛ рдФрд░ рдЧрд╣рд░рд╛рдИ рд╕реЗ рдкрдврд╝рдирд╛ рд╣реЛрдЧрд╛ рдПрдХреНрд╕рдЯрд░реНрдирд▓ рдХреЗ рд▓рд┐рдП рдЕрд▓рдЧ-рдЕрд▓рдЧ рдЙрдкрдпреЛрдЧ рдХреЗ рдорд╛рдорд▓реЗ ternред

рдлрд┐рд▓рд╣рд╛рд▓ рдореИрдВ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХреЗрд╡рд▓ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ рдХрд┐ рдмрд╛рд╣рд░реА рдХреЛ рдЦрд╛рд▓реА рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд░реАрд╕реЗрдЯ рдХрд░реЗрдВ рддрд╛рдХрд┐ рдореБрдЭреЗ рд╕рдм рдХреБрдЫ рдЖрд╕рд╛рдиреА рд╕реЗ рдкреЛрд░реНрдЯреЗрдмрд▓ рдРрдк рдореЗрдВ рдорд┐рд▓ рдЬрд╛рдП рдЬреЛ рд░рдирдЯрд╛рдЗрдо рдХреЗ рджреМрд░рд╛рди рдиреЛрдб_рдореЙрдбрд▓ рдкрд░ рдирд┐рд░реНрднрд░ рди рд╣реЛред

рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕