diff --git a/jest.config.js b/jest.config.js
index cc7eddadc5..7cea379547 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -135,7 +135,14 @@ const jestConfig = {
configureProject('upward-js', 'Upward JS', () => ({
testEnvironment: 'node'
})),
- configureProject('venia-concept', 'Venia Storefront', testVenia),
+ configureProject('venia-concept', 'Venia Storefront', inPackage => {
+ const veniaConceptConfig = testVenia(inPackage);
+ veniaConceptConfig.setupFiles = [
+ ...veniaConceptConfig.setupFilesAfterEnv,
+ inPackage('scripts/fetch-mock.js')
+ ];
+ return veniaConceptConfig;
+ }),
configureProject('venia-ui', 'Venia UI', testVenia),
// Test any root CI scripts as well, to ensure stable CI behavior.
configureProject('scripts', 'CI Scripts', () => ({
@@ -175,7 +182,10 @@ const jestConfig = {
'__fixtures__',
'__helpers__',
'__snapshots__'
- ]
+ ],
+ globals: {
+ STORE_NAME: 'Venia'
+ }
};
if (process.env.npm_lifecycle_event === 'test:ci') {
diff --git a/packages/pwa-buildpack/lib/WebpackTools/PWADevServer.js b/packages/pwa-buildpack/lib/WebpackTools/PWADevServer.js
index 8fb052d5da..03a42a64b2 100644
--- a/packages/pwa-buildpack/lib/WebpackTools/PWADevServer.js
+++ b/packages/pwa-buildpack/lib/WebpackTools/PWADevServer.js
@@ -55,7 +55,6 @@ const PWADevServer = {
contentBase: false, // UpwardDevServerPlugin serves static files
compress: true,
hot: true,
- writeToDisk: true,
watchOptions: {
// polling is CPU intensive - provide the option to turn it on if needed
poll: !!parseInt(devServer.watchOptionsUsePolling) || false
@@ -237,7 +236,7 @@ const PWADevServer = {
new UpwardDevServerPlugin(
webpackDevServerOptions,
process.env,
- path.resolve(webpackConfig.output.path, upwardPath)
+ path.resolve(webpackConfig.context, upwardPath)
)
);
diff --git a/packages/venia-concept/package.json b/packages/venia-concept/package.json
index 59b69ceb5b..8c0216a26a 100644
--- a/packages/venia-concept/package.json
+++ b/packages/venia-concept/package.json
@@ -87,9 +87,11 @@
"graphql-cli": "~3.0.11",
"graphql-cli-validate-magento-pwa-queries": "~1.0.0",
"graphql-tag": "~2.10.1",
+ "html-webpack-plugin": "~3.2.0",
"informed": "~2.1.13",
"lodash.over": "~4.7.0",
"memoize-one": "~5.0.0",
+ "node-fetch": "~2.6.0",
"prettier": "~1.16.4",
"prop-types": "~15.7.2",
"react": "~16.9.0",
diff --git a/packages/venia-concept/scripts/fetch-mock.js b/packages/venia-concept/scripts/fetch-mock.js
new file mode 100644
index 0000000000..c158ca4267
--- /dev/null
+++ b/packages/venia-concept/scripts/fetch-mock.js
@@ -0,0 +1 @@
+global.fetch = require('jest-fetch-mock');
diff --git a/packages/venia-concept/src/MediaBackendURLFetcherPlugin.js b/packages/venia-concept/src/MediaBackendURLFetcherPlugin.js
new file mode 100644
index 0000000000..877b71abe3
--- /dev/null
+++ b/packages/venia-concept/src/MediaBackendURLFetcherPlugin.js
@@ -0,0 +1,40 @@
+const fetch = require('node-fetch');
+
+const getMediaURL = () =>
+ fetch(new URL('graphql', process.env.MAGENTO_BACKEND_URL).toString(), {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ query: 'query { storeConfig { secure_base_media_url } }'
+ })
+ })
+ .then(result => result.json())
+ .then(json => json.data.storeConfig.secure_base_media_url)
+ .catch(err => {
+ console.error(err);
+ return '';
+ });
+
+/**
+ * Webpack plugin that makes GraphQL call to get the Media
+ * Backend URL which is needed by WebpackHTMLPlugin to be
+ * replace in template.html. This is done by fetching the URL
+ * from GraphQL and placing it in global.MAGENTO_MEDIA_BACKEND_URL
+ * for the WebpackHTMLPlugin to pickup.
+ */
+class MediaBackendURLFetcherPlugin {
+ apply(compiler) {
+ compiler.hooks.emit.tapPromise(
+ 'MediaBackendURLFetcherPlugin',
+ () =>
+ new Promise(resolve => {
+ getMediaURL().then(url => {
+ global.MAGENTO_MEDIA_BACKEND_URL = url;
+ resolve();
+ });
+ })
+ );
+ }
+}
+
+module.exports = MediaBackendURLFetcherPlugin;
diff --git a/packages/venia-concept/src/__tests__/MediaBackendURLFetcherPlugin.spec.js b/packages/venia-concept/src/__tests__/MediaBackendURLFetcherPlugin.spec.js
new file mode 100644
index 0000000000..98b4004cc0
--- /dev/null
+++ b/packages/venia-concept/src/__tests__/MediaBackendURLFetcherPlugin.spec.js
@@ -0,0 +1,53 @@
+const MediaBackendURLFetcherPlugin = require('../MediaBackendURLFetcherPlugin');
+
+const tapPromise = jest.fn();
+
+const compiler = {
+ hooks: {
+ emit: {
+ tapPromise
+ }
+ }
+};
+
+beforeEach(() => {
+ process.env.MAGENTO_BACKEND_URL =
+ 'https://venia-cicd-lrov2hi-mfwmkrjfqvbjk.us-4.magentosite.cloud/';
+ fetch.resetMocks();
+});
+
+test('The plugin prototype should have apply function in its Prototype', () => {
+ expect(
+ MediaBackendURLFetcherPlugin.prototype.hasOwnProperty('apply')
+ ).toBeTruthy();
+});
+
+test('tapPromise function should be called on compiler hooks', () => {
+ const mediaBackendURLFetcher = new MediaBackendURLFetcherPlugin();
+ mediaBackendURLFetcher.apply(compiler);
+ expect(tapPromise.mock.calls[0][0]).toBe('MediaBackendURLFetcherPlugin');
+ expect(tapPromise.mock.calls[0][1] instanceof Function).toBeTruthy();
+});
+
+test('second argument to tapPromise should return a Promise which when resolved should set global.MAGENTO_MEDIA_BACKEND_URL to a URL', () => {
+ const expectedMediaURL =
+ 'https://venia-cicd-lrov2hi-mfwmkrjfqvbjk.us-4.magentosite.cloud/media/';
+ fetch.mockResponseOnce(
+ JSON.stringify({
+ data: {
+ storeConfig: {
+ secure_base_media_url: expectedMediaURL
+ }
+ }
+ })
+ );
+
+ expect(global.MAGENTO_MEDIA_BACKEND_URL).toBe(undefined);
+
+ const mediaBackendURLFetcher = new MediaBackendURLFetcherPlugin();
+ mediaBackendURLFetcher.apply(compiler);
+
+ return tapPromise.mock.calls[0][1].call().then(() => {
+ expect(global.MAGENTO_MEDIA_BACKEND_URL).toBe(expectedMediaURL);
+ });
+});
diff --git a/packages/venia-concept/template.html b/packages/venia-concept/template.html
new file mode 100644
index 0000000000..d218d090c8
--- /dev/null
+++ b/packages/venia-concept/template.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+ Home Page - <%= STORE_NAME %>
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/venia-concept/webpack.config.js b/packages/venia-concept/webpack.config.js
index 16aea9bfcd..4a35e858c9 100644
--- a/packages/venia-concept/webpack.config.js
+++ b/packages/venia-concept/webpack.config.js
@@ -1,4 +1,7 @@
const { configureWebpack } = require('@magento/pwa-buildpack');
+const { DefinePlugin } = require('webpack');
+const HTMLWebpackPlugin = require('html-webpack-plugin');
+const MediaBackendUrlFetcherPlugin = require('./src/MediaBackendURLFetcherPlugin');
module.exports = async env => {
const config = await configureWebpack({
@@ -36,13 +39,33 @@ module.exports = async env => {
env
});
- // configureWebpack() returns a regular Webpack configuration object.
- // You can customize the build by mutating the object here, as in
- // this example:
+ /**
+ * configureWebpack() returns a regular Webpack configuration object.
+ * You can customize the build by mutating the object here, as in
+ * this example. Since it's a regular Webpack configuration, the object
+ * supports the `module.noParse` option in Webpack, documented here:
+ * https://webpack.js.org/configuration/module/#modulenoparse
+ */
config.module.noParse = [/braintree\-web\-drop\-in/];
- // Since it's a regular Webpack configuration, the object supports the
- // `module.noParse` option in Webpack, documented here:
- // https://webpack.js.org/configuration/module/#modulenoparse
+ config.plugins = [
+ ...config.plugins,
+ new DefinePlugin({
+ /**
+ * Make sure to add the same constants to
+ * the globals object in jest.config.js.
+ */
+ STORE_NAME: JSON.stringify('Venia')
+ }),
+ new MediaBackendUrlFetcherPlugin(),
+ new HTMLWebpackPlugin({
+ filename: 'index.html',
+ template: './template.html',
+ minify: {
+ collapseWhitespace: true,
+ removeComments: true
+ }
+ })
+ ];
return config;
};
diff --git a/packages/venia-ui/lib/RootComponents/Product/product.js b/packages/venia-ui/lib/RootComponents/Product/product.js
index 089c8ca83f..a809e963e5 100644
--- a/packages/venia-ui/lib/RootComponents/Product/product.js
+++ b/packages/venia-ui/lib/RootComponents/Product/product.js
@@ -61,7 +61,7 @@ class Product extends Component {
return (
- {`${product.name} - Venia`}
+ {`${product.name} - ${STORE_NAME}`}
{
return instance;
};
+beforeAll(() => {
+ global.STORE_NAME = 'Venia';
+});
+
afterAll(() => window.location.reload.mockRestore());
test('renders a full page with onlineIndicator and routes', () => {
diff --git a/packages/venia-ui/lib/components/App/app.js b/packages/venia-ui/lib/components/App/app.js
index 9974fd4113..22bb1d7777 100644
--- a/packages/venia-ui/lib/components/App/app.js
+++ b/packages/venia-ui/lib/components/App/app.js
@@ -123,7 +123,7 @@ const App = props => {
return (
- {'Home Page - Venia'}
+ {`Home Page - ${STORE_NAME}`}
1.10.0":
version "1.10.5"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
@@ -8465,7 +8482,7 @@ hash.js@^1.0.0, hash.js@^1.0.3:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
-he@^1.2.0:
+he@1.2.x, he@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
@@ -8565,6 +8582,19 @@ html-entities@^1.2.0:
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"
integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=
+html-minifier@^3.2.3:
+ version "3.5.21"
+ resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c"
+ integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==
+ dependencies:
+ camel-case "3.0.x"
+ clean-css "4.2.x"
+ commander "2.17.x"
+ he "1.2.x"
+ param-case "2.1.x"
+ relateurl "0.2.x"
+ uglify-js "3.4.x"
+
html-minifier@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-4.0.0.tgz#cca9aad8bce1175e02e17a8c33e46d8988889f56"
@@ -8590,6 +8620,19 @@ html-webpack-plugin@^4.0.0-beta.2:
tapable "^1.1.3"
util.promisify "1.0.0"
+html-webpack-plugin@~3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b"
+ integrity sha1-sBq71yOsqqeze2r0SS69oD2d03s=
+ dependencies:
+ html-minifier "^3.2.3"
+ loader-utils "^0.2.16"
+ lodash "^4.17.3"
+ pretty-error "^2.0.2"
+ tapable "^1.0.0"
+ toposort "^1.0.0"
+ util.promisify "1.0.0"
+
htmlparser2@^3.3.0, htmlparser2@^3.9.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
@@ -10404,6 +10447,16 @@ loader-utils@1.2.3, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.
emojis-list "^2.0.0"
json5 "^1.0.1"
+loader-utils@^0.2.16:
+ version "0.2.17"
+ resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348"
+ integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=
+ dependencies:
+ big.js "^3.1.3"
+ emojis-list "^2.0.0"
+ json5 "^0.5.0"
+ object-assign "^4.0.1"
+
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
@@ -10575,7 +10628,7 @@ lodash@4.17.5:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
integrity sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==
-lodash@^4.0.0, lodash@^4.11.1, lodash@^4.15.0, lodash@^4.16.0, lodash@^4.16.4, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@~4.17.11:
+lodash@^4.0.0, lodash@^4.11.1, lodash@^4.15.0, lodash@^4.16.0, lodash@^4.16.4, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@~4.17.11:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
@@ -11284,7 +11337,7 @@ node-fetch@^1.0.1, node-fetch@^1.7.3:
encoding "^0.1.11"
is-stream "^1.0.1"
-node-fetch@^2.1.2, node-fetch@^2.2.0, node-fetch@^2.3.0:
+node-fetch@^2.1.2, node-fetch@^2.2.0, node-fetch@^2.3.0, node-fetch@~2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
@@ -12060,7 +12113,7 @@ parallel-transform@^1.1.0:
inherits "^2.0.3"
readable-stream "^2.1.5"
-param-case@^2.1.0, param-case@^2.1.1:
+param-case@2.1.x, param-case@^2.1.0, param-case@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc=
@@ -12621,7 +12674,7 @@ pretty-bytes@^5.1.0:
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2"
integrity sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg==
-pretty-error@^2.1.1:
+pretty-error@^2.0.2, pretty-error@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3"
integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=
@@ -13706,7 +13759,7 @@ regjsparser@^0.6.0:
dependencies:
jsesc "~0.5.0"
-relateurl@^0.2.7:
+relateurl@0.2.x, relateurl@^0.2.7:
version "0.2.7"
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
@@ -15437,6 +15490,11 @@ toidentifier@1.0.0:
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
+toposort@^1.0.0:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029"
+ integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk=
+
tough-cookie@^2.3.3, tough-cookie@^2.3.4:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
@@ -15549,6 +15607,14 @@ ua-parser-js@^0.7.18:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.20.tgz#7527178b82f6a62a0f243d1f94fd30e3e3c21098"
integrity sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw==
+uglify-js@3.4.x:
+ version "3.4.10"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
+ integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==
+ dependencies:
+ commander "~2.19.0"
+ source-map "~0.6.1"
+
uglify-js@^3.1.4, uglify-js@^3.5.1:
version "3.6.0"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5"