Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V4P2: feat(buildpack): Add Webpack configurator facade #1498

Merged
merged 10 commits into from Aug 8, 2019

Conversation

zetlen
Copy link
Contributor

@zetlen zetlen commented Jul 31, 2019

Description

Pull complex Webpack configuration out of the Venia app and put it in an opinionated Buildpack utility.

This replaces most of the large webpack.config.js file in Venia. The entire PWA Studio runtime depends on the logic in that file; Peregrine, Buildpack, and UpwardJS make assumptions based on that file's configuration, which is an antipattern.

This PR adds a configureWebpack() function which returns a Promise for a Webpack configuration object. It takes parameters for a subset of configuration options, including:

  • context: Absolute path of the project root to ensure consistent behavior no matter the working directory; if webpack.config.js is in project root then this should be simply __dirname.
  • vendor: Names of modules to bundle into a commons bundle. Set to a sensible default list in Venia.
  • special: Map of modules to flag as containing 'special' functionality, to be treated differently by the Webpack plugin and loader chain. Keys should be module names. Values should be one or more of the following flags:
    • esModules: Denotes that JS code for this module must adhere to the babel-preset-peregrine Babel preset. Code will be imported as modules using the same loader chain as local application code, enabling tree-shaking on node_modules dependencies.
    • cssModules: Denotes that CSS code for this module must assign all CSS imports to a variable, to receive an object of dynamically generated CSS classes. Style will be imported using the same loader chain as local application stylesheets, enabling total CSS modularity.
    • graphQLQueries: Denotes that JS code for this module contains GraphQL queries embedded in calls to the graphql-tag template literal. GraphQL will be preprocessed using the same loader chain as local application code.
    • rootComponents: Denotes that this module includes a src/RootComponents or lib/RootComponents directory containing RootComponents to be processed by the RootComponentsPlugin. These RootComponents will be added to the map of all RootComponent mappings available to the application.
    • upward: Denotes that this module includes an upward.yml file at root declaring some subset of server-side behavior it requires and/or supplies. This file will be merged, along with the upward.yml for any other modules you configure with this flag. It will merge down to a final upward.yml file produced as a build asset.

Also adds a lot of debuggability to Buildpack, and cleans up some of its APIs.

Closes #1502 along with #1497 and #1499.

ℹ️ Docs and tests TODO.

Verification Steps

  1. In a fresh repo, run yarn run build.
  2. Examine packages/venia-concept/webpack.config.js.
  3. Try adjusting the configuration flags to see what happens.

Proposed Labels for Change Type/Package

  • major (e.g x.0.0 - a breaking change)
  • minor (e.g 0.x.0 - a backwards compatible addition)
  • patch (e.g 0.0.x - a bug fix)

Checklist:

  • I have updated the documentation accordingly, if necessary.
  • I have added tests to cover my changes, if necessary.

@vercel
Copy link

vercel bot commented Jul 31, 2019

This pull request is automatically deployed with Now.
To access deployments, click Details below or on the icon next to each push.

Latest deployment for this branch: https://pwa-studio-git-zetlen-v4apip2-webpack-facade.mmansoor.now.sh

@zetlen zetlen changed the base branch from develop to zetlen/v4api/p1-use-relative-imports July 31, 2019 14:06
@PWAStudioBot
Copy link
Contributor

PWAStudioBot commented Jul 31, 2019

Warnings
⚠️ Found the word "TODO" in the PR description. Just letting you know incase you forgot :)
Messages
📖 We are currently working on automating the PR metadata checks. Until that time, you may see failures related to labels/description/linked issues/etc even if you have fixed the problem. Failures will persist until the next push (assuming they are fixed).

Generated by 🚫 dangerJS against f6f3909

@coveralls
Copy link

coveralls commented Jul 31, 2019

Coverage Status

Coverage decreased (-2.5%) to 77.232% when pulling 984ef97 on zetlen/v4api/p2-webpack-facade into e629de4 on zetlen/v4api/p1-use-relative-imports.

@zetlen zetlen changed the title feat(buildpack): Add Webpack configurator facade V4P2: feat(buildpack): Add Webpack configurator facade Jul 31, 2019
@zetlen zetlen force-pushed the zetlen/v4api/p2-webpack-facade branch from 94383d6 to 6733cec Compare July 31, 2019 15:27
@zetlen zetlen force-pushed the zetlen/v4api/p2-webpack-facade branch from 6733cec to 87edf4d Compare July 31, 2019 15:39
@zetlen zetlen force-pushed the zetlen/v4api/p2-webpack-facade branch from 87edf4d to 984ef97 Compare July 31, 2019 16:12
@@ -7,6 +7,8 @@ jest.mock('../../Utilities/configureHost');
const fs = require('fs');
jest.spyOn(fs, 'readFile');

const mockContext = require('path').resolve(__dirname, '../../../../../');

const debugErrorMiddleware = require('debug-error-middleware');
const waitForExpect = require('wait-for-expect');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This dependency no longer exists, this line can be removed. I believe this is the only reference to waitForExpect.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found more in tests for useQuery, useRestApi, and magentoRouteHandler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the dependency is still there. Instead of listing it at root I added it to Buildpack and Peregrine, the two places it's used.

@@ -60,7 +60,8 @@ const reducer = (prevState = initialState, action = {}) => {
}
};

const ToastContext = createContext();
const ToastContext =
window.__ToastContext || (window.__ToastContext = createContext());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this creating multiple toast contexts previously?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this line confuses me, especially since window.__ToastContext is not referenced anywhere else. If there's a bug with this particular context object, we should diagnose it and fix it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how this change made it in here: must have been a bad old rebase. Removed.

"name": "DEVELOPER_DEBUG_BUNDLES",
"type": "bool",
"desc": "Build developer-mode bundles without adding hot reloading or launching the dev server. Disables minification and enables all Webpack debug settings for maximum code readability.",
"default": false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just developer mode?

Copy link
Contributor

@sirugh sirugh Aug 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if I set this to true, yarn watch:all and yarn watch:venia no longer serve properly. Not sure what's going on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's busted because of some bad choices in the informed library.

clearTimeout(timeout);
resolve({
key: certBuffers.key.toString('utf8'),
cert: certBuffers.cert.toString('utf8')
});
} catch (e) {
clearTimeout(timeout);
debug('failure! cleared UI timeout for getCert("%s")', hostname);
debug(e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like there are some debug calls you can clear out of here if you want.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I joined those two together, but debug calls are cheap and they should be plentiful.

TRAPPING_MONK: 'level 1',
TRAPPING_ELF: 'level 2',
GEWGAW_PALADIN: 'level 3',
GEWGAW_ROGUE: 'level 4',
MUSTANG_SALLY: 'level a million'
GEWGAW_ROGUE: 'level 4'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is hilarious lol

}),
new UpwardIncludePlugin({
upwardDirs: hasFlag('upward')
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was kind of expecting us not to include the UpwardIncludePlugin at all if hasFlag('upward') is false.
Does the plugin fail gracefully without any upwardDirs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was kind of expecting us not to include the UpwardIncludePlugin at all if hasFlag('upward') is false.

Minor correction: hasFlag("upward") can't be false; it would return [] if no packages had that flag.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@supernova-at Good catch! We should always require an upward file in the app itself.

const UpwardPlugin = require('../UpwardPlugin');
const UpwardDevServerPlugin = require('../UpwardDevServerPlugin');

const mockContext = require('path').resolve(__dirname, '../../../../../../');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the path to our project root I'm guessing? If so, may want to drop a comment or rename the variable projectRoot or something

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment would suffice, I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment added.


### `ConfigureWebpackOptions

TODO
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just highlighting this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see this filled out before we merge. At least copy in what was written in the PR description :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I'll update you.


## Example

TODO
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just highlighting this

@@ -78,13 +78,37 @@ The Venia Adapter wraps around Venia components to satisfy any implicit external

### Venia drivers

The [`@magento/venia-drivers`][] dependency is a _virtual module_ for centralizing the external dependencies of Venia components that use them, such as GraphQL clients and Redux stores.
Instead of importing these dependencies directly, Venia components import them from `src/drivers`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this repeating L92 & L93?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we drop L92 & L93?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since @jcalcaben wrote L92 and L93 I'd rather drop these lines, which I've done.

@supernova-at supernova-at self-assigned this Jul 31, 2019
@cherdman cherdman added the version: Minor This changeset includes functionality added in a backwards compatible manner. label Jul 31, 2019
@jimbo jimbo self-assigned this Jul 31, 2019
Copy link
Contributor

@jimbo jimbo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

18/72 files viewed. Left some comments for now.

"@babel/preset-env": "^7.3.4",
"@babel/runtime": "^7.4.2",
"babel-plugin-dynamic-import-node": "^2.2.0",
"babel-plugin-graphql-tag": "^2.0.0"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this change (all of these dependencies moving from tilde to caret) intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this was a mistake. Reverted!

"jsnext:main": "lib/index.js",
"es2015": "lib/index.js",
"sideEffects": false
"esnext": "lib/index.js",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this supersede jsnext:main?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The precedence order is in MagentoResolver, under browserFields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original idea was to support proposals for this convention from several different projects, for maximum compatibility. Rollup, SystemJS, etc. I think that's beyond scope, so we can just set our own aliases.

modules: [options.paths.root, 'node_modules'],
mainFiles: ['index'],
extensions: ['.mjs', '.js', '.json', '.graphql']
mainFields: ['esnext', 'es2015', 'module', 'browser', 'main'],
extensions: ['.wasm', '.mjs', '.js', '.json', '.graphql']
Copy link
Contributor

@jimbo jimbo Jul 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, one optimization to make webpack resolve a lot faster is to cut this field down to just [.js], precluding the need to resolve them in order. I'm surprised we aren't already doing it, actually; could we do it here?

Not only are the other extensions rarely imported, we already tend to include them explicitly for clarity's sake.

// safe to assume `./foo` refers to `foo.js` or `foo/index.js`
import Foo from "./foo"

// by explicitly appending `.css`, we avoid ambiguity or surprise
import defaultClasses from "./foo.css"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not seeing that advantage reliably when I try that. With no cache it's always around 50 seconds, and with full cache it's always around 20.

Copy link
Contributor

@sirugh sirugh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also leaving evening feedback so far. Still have more to look at.

@@ -60,7 +60,8 @@ const reducer = (prevState = initialState, action = {}) => {
}
};

const ToastContext = createContext();
const ToastContext =
window.__ToastContext || (window.__ToastContext = createContext());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary? I feel like you told me you worked this out in the past and didn't have to do this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't even supposed to be here, I've removed it :)

parsed: {}
});
loadEnvCliBuilder.handler({ directory: '.' });
expect(process.exit).toHaveBeenCalled();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests in aws pipeline are failing on this assertion. Can you investigate?

@@ -5,7 +5,8 @@ const { zipObject } = require('lodash');
const isPrimitive = require('./isPrimitive');

class ResolverVisitor {
constructor(io, rootDefinition, context) {
constructor(upwardPath, io, rootDefinition, context) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically breaking change to upward-js, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed we are modifying lots of input args. In general I think it's safer to append new arguments instead of prepend.

Also, some docs on this file would go a long way :D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I suppose this shouldn't be a prepend :/ I'll refactor it


// A DevServer generates its own unique output path at startup. It needs
// to assign the main outputPath to this value as well.
// '@magento/venia-library': {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

placeholder?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For demo purposes.

jimbo
jimbo previously approved these changes Aug 7, 2019
packages/pwa-buildpack/package.json Outdated Show resolved Hide resolved
Co-Authored-By: Jimmy Sanford <jimbo@users.noreply.github.com>
@zetlen
Copy link
Contributor Author

zetlen commented Aug 7, 2019

Hey @jcalcaben, I'd like your overt approval before merging this.

Copy link
Contributor

@jcalcaben jcalcaben left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc changes and drafts look good to me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pkg:babel-preset-peregrine pkg:graphql-cli-validate-magento-pwa-queries pkg:peregrine pkg:pwa-buildpack pkg:upward-js Pertains to upward-js reference implementation of UPWARD. pkg:upward-spec Pertains to UPWARD specification package. pkg:venia-concept version: Minor This changeset includes functionality added in a backwards compatible manner.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[feature]: Make Venia components less dependent on specific Webpack config
9 participants