diff --git a/e2e-tests/production-runtime/cypress/integration/assets.js b/e2e-tests/production-runtime/cypress/integration/assets.js new file mode 100644 index 0000000000000..a25e13d253fd7 --- /dev/null +++ b/e2e-tests/production-runtime/cypress/integration/assets.js @@ -0,0 +1,36 @@ +describe(`webpack assets`, () => { + beforeEach(() => { + cy.intercept("/gatsby-astronaut.png").as("static-folder-image") + // Should load two files: normal and italic + cy.intercept("/static/merriweather-latin-300**.woff2").as("font") + cy.intercept("/static/gatsby-astronaut-**.png").as("img-import") + cy.visit(`/assets/`).waitForRouteChange() + }) + + // Service worker is handling requests so this one is cached by previous runs + if (!Cypress.env(`TEST_PLUGIN_OFFLINE`)) { + it(`should only create one font file (no duplicates with different hashes)`, () => { + // Check that there is no duplicate files (should have italic as second request, not another normal font) + cy.wait("@font").should(req => { + expect(req.response.url).to.match(/merriweather-latin-300-/i) + }) + cy.wait("@font").should(req => { + expect(req.response.url).to.match(/merriweather-latin-300italic-/i) + }) + }) + it(`should load image import`, () => { + cy.wait("@img-import").should(req => { + expect(req.response.statusCode).to.be.gte(200).and.lt(400) + }) + }) + it(`should load file import`, () => { + cy.getTestElement('assets-pdf-import').should('have.attr', 'href').and('match', /\/static\/pdf-example-.*\.pdf/i) + }) + } + + it(`should load static folder asset`, () => { + cy.wait("@static-folder-image").should(req => { + expect(req.response.statusCode).to.be.gte(200).and.lt(400) + }) + }) +}) diff --git a/e2e-tests/production-runtime/package.json b/e2e-tests/production-runtime/package.json index 0f24c4e07d504..c29748947caf0 100644 --- a/e2e-tests/production-runtime/package.json +++ b/e2e-tests/production-runtime/package.json @@ -6,18 +6,18 @@ "dependencies": { "babel-plugin-search-and-replace": "^1.1.0", "cypress": "^6.5.0", - "gatsby": "^3.0.0-next.6", - "gatsby-cypress": "^1.3.0", - "gatsby-plugin-image": "^1.0.0-next.5", - "gatsby-plugin-less": "^5.1.0-next.2", - "gatsby-plugin-manifest": "^3.0.0-next.0", - "gatsby-plugin-offline": "^4.0.0-next.1", - "gatsby-plugin-react-helmet": "^4.0.0-next.0", - "gatsby-plugin-sass": "^4.1.0-next.2", - "gatsby-plugin-sharp": "^3.0.0-next.5", - "gatsby-plugin-stylus": "^3.1.0-next.2", + "gatsby": "^4.1.6", + "gatsby-cypress": "^2.1.0", + "gatsby-plugin-image": "^2.1.3", + "gatsby-plugin-less": "^6.1.0", + "gatsby-plugin-manifest": "^4.1.4", + "gatsby-plugin-offline": "^5.1.4", + "gatsby-plugin-react-helmet": "^5.1.0", + "gatsby-plugin-sass": "^5.1.1", + "gatsby-plugin-sharp": "^4.1.4", + "gatsby-plugin-stylus": "^4.1.0", "gatsby-seo": "^0.1.0", - "gatsby-source-filesystem": "^3.3.0", + "gatsby-source-filesystem": "^4.1.3", "glob": "^7.1.3", "react": "^16.9.0", "react-dom": "^16.9.0", @@ -32,10 +32,11 @@ "scripts": { "build": "cross-env CYPRESS_SUPPORT=y gatsby build", "build:offline": "cross-env TEST_PLUGIN_OFFLINE=y CYPRESS_SUPPORT=y gatsby build", - "develop": "gatsby develop", + "develop": "cross-env CYPRESS_SUPPORT=y gatsby develop", "format": "prettier --write '**/*.js' --ignore-path .gitignore", "serve": "gatsby serve", "start": "npm run develop", + "clean": "gatsby clean", "test": "npm run build && npm run start-server-and-test && npm run test-env-vars", "test:offline": "npm run build:offline && yarn start-server-and-test:offline && npm run test-env-vars", "test-env-vars": " node __tests__/env-vars.js", @@ -51,7 +52,7 @@ "devDependencies": { "cross-env": "^5.2.0", "fs-extra": "^7.0.1", - "gatsby-core-utils": "^2.12.0", + "gatsby-core-utils": "^3.1.3", "is-ci": "^2.0.0", "prettier": "2.0.4", "start-server-and-test": "^1.7.1" @@ -59,8 +60,5 @@ "repository": { "type": "git", "url": "https://github.com/gatsbyjs/gatsby-starter-default" - }, - "resolutions": { - "graphql-config": "3.3.0" } } diff --git a/e2e-tests/production-runtime/src/files/pdf-example.pdf b/e2e-tests/production-runtime/src/files/pdf-example.pdf new file mode 100644 index 0000000000000..33114e14c25e5 Binary files /dev/null and b/e2e-tests/production-runtime/src/files/pdf-example.pdf differ diff --git a/e2e-tests/production-runtime/src/images/gatsby-astronaut.png b/e2e-tests/production-runtime/src/images/gatsby-astronaut.png new file mode 100644 index 0000000000000..da58ece0a8c5b Binary files /dev/null and b/e2e-tests/production-runtime/src/images/gatsby-astronaut.png differ diff --git a/e2e-tests/production-runtime/src/index.css b/e2e-tests/production-runtime/src/index.css index 4e165ba8657fd..bc6b26fb45c76 100644 --- a/e2e-tests/production-runtime/src/index.css +++ b/e2e-tests/production-runtime/src/index.css @@ -15,6 +15,19 @@ font-size: 18px; } +.merriweather-300 { + font-family: "Merriweather"; + font-weight: 300; + font-size: 18px; +} + +.merriweather-300-italic { + font-family: "Merriweather"; + font-weight: 300; + font-size: 18px; + font-style: italic; +} + .dog-background-flip { background: url("//localhost:9000/dog-thumbnail-flip.jpg"); width: 640px; diff --git a/e2e-tests/production-runtime/src/pages/assets.js b/e2e-tests/production-runtime/src/pages/assets.js new file mode 100644 index 0000000000000..67f85ee166738 --- /dev/null +++ b/e2e-tests/production-runtime/src/pages/assets.js @@ -0,0 +1,16 @@ +import * as React from "react" +import Layout from "../components/layout" +import astronaut from "../images/gatsby-astronaut.png" +import pdf from "../files/pdf-example.pdf" + +const Assets = () => ( + +

Font

+

Font Italic

+ Gatsby Astronaut Static Folder + Gatsby Astronaut + Download PDF +
+) + +export default Assets diff --git a/e2e-tests/production-runtime/static/gatsby-astronaut.png b/e2e-tests/production-runtime/static/gatsby-astronaut.png new file mode 100644 index 0000000000000..da58ece0a8c5b Binary files /dev/null and b/e2e-tests/production-runtime/static/gatsby-astronaut.png differ diff --git a/integration-tests/artifacts/gatsby-browser.js b/integration-tests/artifacts/gatsby-browser.js index af169d50fce67..1a0a374d24012 100644 --- a/integration-tests/artifacts/gatsby-browser.js +++ b/integration-tests/artifacts/gatsby-browser.js @@ -2,6 +2,9 @@ const React = require(`react`) const { useMoreInfoQuery } = require("./src/hooks/use-more-info-query") const Github = require(`./src/components/github`).default +// global css import (make sure warm rebuild doesn't invalidate every file when css is imported) +require("./imported.css") + exports.wrapRootElement = ({ element }) => { return ( <> diff --git a/integration-tests/artifacts/imported.css b/integration-tests/artifacts/imported.css new file mode 100644 index 0000000000000..50c8ae6828d6e --- /dev/null +++ b/integration-tests/artifacts/imported.css @@ -0,0 +1,3 @@ +.foo { + background: blue; +} diff --git a/packages/gatsby-plugin-less/src/gatsby-node.js b/packages/gatsby-plugin-less/src/gatsby-node.js index 0845a60649f21..767e367fcf651 100644 --- a/packages/gatsby-plugin-less/src/gatsby-node.js +++ b/packages/gatsby-plugin-less/src/gatsby-node.js @@ -34,6 +34,7 @@ exports.onCreateWebpackConfig = ( } const lessRuleModules = { test: /\.module\.less$/, + // TODO(v5): Remove obsolete modules option from miniCssExtract use: [ !isSSR && loaders.miniCssExtract({ diff --git a/packages/gatsby-plugin-netlify-cms/package.json b/packages/gatsby-plugin-netlify-cms/package.json index 9d7750f6a4890..1c848bcd09d85 100644 --- a/packages/gatsby-plugin-netlify-cms/package.json +++ b/packages/gatsby-plugin-netlify-cms/package.json @@ -14,7 +14,7 @@ "html-webpack-skip-assets-plugin": "^1.0.3", "html-webpack-tags-plugin": "^3.0.2", "lodash": "^4.17.21", - "mini-css-extract-plugin": "1.6.2", + "mini-css-extract-plugin": "^2.4.4", "netlify-identity-widget": "^1.9.2" }, "devDependencies": { diff --git a/packages/gatsby-plugin-postcss/src/gatsby-node.js b/packages/gatsby-plugin-postcss/src/gatsby-node.js index 98ef0bfbb7a58..366a6c541482a 100644 --- a/packages/gatsby-plugin-postcss/src/gatsby-node.js +++ b/packages/gatsby-plugin-postcss/src/gatsby-node.js @@ -50,6 +50,7 @@ exports.onCreateWebpackConfig = ( } const postcssRuleModules = { test: MODULE_CSS_PATTERN, + // TODO(v5): Remove obsolete modules option from miniCssExtract use: [ !isSSR && loaders.miniCssExtract({ diff --git a/packages/gatsby-plugin-sass/src/gatsby-node.js b/packages/gatsby-plugin-sass/src/gatsby-node.js index dc8bd2f05be7b..0512a17869a53 100644 --- a/packages/gatsby-plugin-sass/src/gatsby-node.js +++ b/packages/gatsby-plugin-sass/src/gatsby-node.js @@ -43,6 +43,7 @@ exports.onCreateWebpackConfig = ( const sassRuleModules = { test: sassRuleModulesTest || /\.module\.s(a|c)ss$/, + // TODO(v5): Remove obsolete modules option from miniCssExtract use: [ !isSSR && loaders.miniCssExtract({ diff --git a/packages/gatsby-plugin-stylus/src/gatsby-node.js b/packages/gatsby-plugin-stylus/src/gatsby-node.js index 2a8e9425c17f0..9036c39d50886 100644 --- a/packages/gatsby-plugin-stylus/src/gatsby-node.js +++ b/packages/gatsby-plugin-stylus/src/gatsby-node.js @@ -54,6 +54,7 @@ exports.onCreateWebpackConfig = ( const stylusRuleModules = { test: /\.module\.styl$/, + // TODO(v5): Remove obsolete modules option from miniCssExtract use: [ !isSSR && loaders.miniCssExtract({ diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 71a80d737199e..b79b326d021d6 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -48,7 +48,7 @@ "cookie": "^0.4.1", "core-js": "^3.17.2", "cors": "^2.8.5", - "css-loader": "^5.2.7", + "css-loader": "^6.5.1", "css-minimizer-webpack-plugin": "^2.0.0", "css.escape": "^1.5.1", "date-fns": "^2.25.0", @@ -108,7 +108,7 @@ "memoizee": "^0.4.15", "micromatch": "^4.0.4", "mime": "^2.5.2", - "mini-css-extract-plugin": "1.6.2", + "mini-css-extract-plugin": "^2.4.4", "mitt": "^1.2.0", "moment": "^2.29.1", "multer": "^1.4.3", diff --git a/packages/gatsby/src/utils/webpack-utils.ts b/packages/gatsby/src/utils/webpack-utils.ts index 92a75778a7b3c..6d1449e4c4add 100644 --- a/packages/gatsby/src/utils/webpack-utils.ts +++ b/packages/gatsby/src/utils/webpack-utils.ts @@ -1,5 +1,5 @@ import * as path from "path" -import { RuleSetRule, WebpackPluginInstance } from "webpack" +import { RuleSetRule, WebpackPluginInstance, Configuration } from "webpack" import { GraphQLSchema } from "graphql" import { Plugin as PostCSSPlugin } from "postcss" import autoprefixer from "autoprefixer" @@ -65,12 +65,16 @@ type CSSModulesOptions = exportOnlyLocals?: boolean } -type MiniCSSExtractLoaderModuleOptions = - | undefined - | boolean - | { - namedExport?: boolean - } +interface IMiniCSSExtractLoaderModuleOptions { + filename?: Required["output"]["filename"] | undefined + chunkFilename?: Required["output"]["chunkFilename"] | undefined + experimentalUseImportModule?: boolean | undefined + ignoreOrder?: boolean | undefined + insert?: string | ((linkTag: any) => void) | undefined + attributes?: Record | undefined + linkType?: string | false | "text/css" | undefined + runtime?: boolean | undefined +} /** * Utils that produce webpack `loader` objects */ @@ -234,27 +238,13 @@ export const createWebpackUtils = ( } }, - miniCssExtract: ( - options: { - modules?: MiniCSSExtractLoaderModuleOptions - } = {} - ) => { - let moduleOptions: MiniCSSExtractLoaderModuleOptions = undefined - + miniCssExtract: (options: IMiniCSSExtractLoaderModuleOptions = {}) => { + // @ts-ignore - legacy modules const { modules, ...restOptions } = options - if (typeof modules === `boolean` && options.modules) { - moduleOptions = { - namedExport: true, - } - } else { - moduleOptions = modules - } - return { loader: MiniCssExtractPlugin.loader, options: { - modules: moduleOptions, ...restOptions, }, } @@ -283,13 +273,15 @@ export const createWebpackUtils = ( loader: require.resolve(`css-loader`), options: { // Absolute urls (https or //) are not send to this function. Only resolvable paths absolute or relative ones. - url: function (url: string): boolean { - // When an url starts with / - if (url.startsWith(`/`)) { - return false - } - - return true + url: { + filter: function (url: string): boolean { + // When an url starts with / + if (url.startsWith(`/`)) { + return false + } + + return true + }, }, sourceMap: !PRODUCTION, modules: modulesOptions, @@ -350,6 +342,7 @@ export const createWebpackUtils = ( } }, + // TODO(v5): Consider removing this (as not used anymore internally) url: (options = {}) => { return { loader: require.resolve(`url-loader`), @@ -545,8 +538,11 @@ export const createWebpackUtils = ( */ rules.fonts = (): RuleSetRule => { return { - use: [loaders.url()], test: /\.(eot|otf|ttf|woff(2)?)(\?.*)?$/, + type: `asset/resource`, + generator: { + filename: `${assetRelativeRoot}[name]-[hash][ext]`, + }, } } @@ -556,8 +552,11 @@ export const createWebpackUtils = ( */ rules.images = (): RuleSetRule => { return { - use: [loaders.url()], test: /\.(ico|svg|jpg|jpeg|png|gif|webp|avif)(\?.*)?$/, + type: `asset/resource`, + generator: { + filename: `${assetRelativeRoot}[name]-[hash][ext]`, + }, } } @@ -567,8 +566,11 @@ export const createWebpackUtils = ( */ rules.media = (): RuleSetRule => { return { - use: [loaders.url()], test: /\.(mp4|webm|ogv|wav|mp3|m4a|aac|oga|flac)$/, + type: `asset/resource`, + generator: { + filename: `${assetRelativeRoot}[name]-[hash][ext]`, + }, } } diff --git a/yarn.lock b/yarn.lock index 7e96d690bda91..3e7c0337fe880 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8260,26 +8260,24 @@ css-list-helpers@^2.0.0: resolved "https://registry.yarnpkg.com/css-list-helpers/-/css-list-helpers-2.0.0.tgz#7cb3d6f9ec9e5087ae49d834cead282806e8818f" integrity sha512-9Bj8tZ0jWbAM3u/U6m/boAzAwLPwtjzFvwivr2piSvyVa3K3rChJzQy4RIHkNkKiZCHrEMWDJWtTR8UyVhdDnQ== -css-loader@^5.2.7: - version "5.2.7" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.7.tgz#9b9f111edf6fb2be5dc62525644cbc9c232064ae" - integrity sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg== +css-loader@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.2.0.tgz#9663d9443841de957a3cb9bcea2eda65b3377071" + integrity sha512-/rvHfYRjIpymZblf49w8jYcRo2y9gj6rV8UroHGmBxKrIyGLokpycyKzp9OkitvqT29ZSpzJ0Ic7SpnJX3sC8g== dependencies: icss-utils "^5.1.0" - loader-utils "^2.0.0" postcss "^8.2.15" postcss-modules-extract-imports "^3.0.0" postcss-modules-local-by-default "^4.0.0" postcss-modules-scope "^3.0.0" postcss-modules-values "^4.0.0" postcss-value-parser "^4.1.0" - schema-utils "^3.0.0" semver "^7.3.5" -css-loader@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.2.0.tgz#9663d9443841de957a3cb9bcea2eda65b3377071" - integrity sha512-/rvHfYRjIpymZblf49w8jYcRo2y9gj6rV8UroHGmBxKrIyGLokpycyKzp9OkitvqT29ZSpzJ0Ic7SpnJX3sC8g== +css-loader@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.5.1.tgz#0c43d4fbe0d97f699c91e9818cb585759091d1b1" + integrity sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ== dependencies: icss-utils "^5.1.0" postcss "^8.2.15" @@ -17123,14 +17121,12 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -mini-css-extract-plugin@1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz#83172b4fd812f8fc4a09d6f6d16f924f53990ca8" - integrity sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q== +mini-css-extract-plugin@^2.4.4: + version "2.4.4" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.4.tgz#c7e5d2d931dcf100ae50ae949ba757c506b54b0f" + integrity sha512-UJ+aNuFQaQaECu7AamlWOBLj2cJ6XSGU4zNiqXeZ7lZLe5VD0DoSPWFbWArXueo+6FZVbgHzpX9lUIaBIDLuYg== dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" - webpack-sources "^1.1.0" + schema-utils "^3.1.0" mini-svg-data-uri@^1.4.3: version "1.4.3" @@ -22984,11 +22980,6 @@ source-list-map@^1.1.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.2.tgz#9889019d1024cce55cdc069498337ef6186a11a1" integrity sha1-mIkBnRAkzOVc3AaUmDN+9hhqEaE= -source-list-map@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== - source-map-js@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" @@ -25837,14 +25828,6 @@ webpack-sources@^0.2.0: source-list-map "^1.1.1" source-map "~0.5.3" -webpack-sources@^1.1.0: - version "1.4.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" - integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - webpack-sources@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.0.tgz#b16973bcf844ebcdb3afde32eda1c04d0b90f89d"