Building Extensions for
Calypso & WordPress.com
Kevin Killingsworth
https://github.com/coderkevin
@coderkevin
Follow along: http://bit.do/quest-for-calypso
Calypso Framework
+
He’s just got two bits of code and he’s banging them together!
The Quest:
Bring Calypso to WooCommerce
https://github.com/Automattic/wc-synchrotron
// package.json
{
…
"devDependencies": {
…
"wp-calypso": "github:automattic/wp-calypso#cafb19f51804ad683c3e852cbaa187654f24529e"
}
}
i18n
CSS Naming
// package.json
"scripts": {
…
"build-synchrotron-strings":
"node_modules/wp-calypso/server/i18n/bin/i18n-cli.js
dist/synchrotron-strings.php
synchrotron_i18n_strings
dist/synchrotron_bundle.js",
"makepot": "grunt dev"
}
// assets/stylesheets/style.scss
.uses-s9n-styles {
// Shared
@import 'shared/reset'; // css reset before the rest of the styles are defined
@import 'shared/functions'; // functions that we've used from Compass, ported over
@import 'shared/functions/functions'; // sass functions for z-index, etc.
…
}
The Black Knight of Webpack
module: {
loaders: [
{
test: /\.jsx?$/,
include: [
path.resolve( __dirname, 'client' ),
path.resolve( __dirname, 'node_modules', 'wp-calypso', 'client' )
]
}
…
}
resolve: {
extensions: [ '', '.js', '.jsx', '.json', '.scss', '.html' ],
modulesDirectories: [ 'node_modules' ],
root: [
path.join( __dirname, 'client' ),
path.join( __dirname, 'node_modules', 'wp-calypso', 'client' )
]
},
sassLoader: {
includePaths: [
path.resolve( __dirname, 'node_modules', 'wp-calypso', 'client' ),
path.resolve( __dirname, 'node_modules', 'wp-calypso', 'assets', 'stylesheets' )
]
},
The Killer Rabbit of NPM
- node_modules
- react
- wp-calypso
- node_modules
- react
"calypso-delete-redundancies": "rimraf node_modules/wp-calypso/node_modules/react",
Framework:
Try a definitive plugin pattern
PR #8103
An initial exploration of what extensions/plugins may look like...
Skilled Calypso Devs
Jetpack!
You can send requests to the WP REST API of any site that has Jetpack 4.5 or newer, and is connected to WordPress.com for the current user. Requests are sent to a special WordPress.com endpoint, which proxies the request, signs it and safely dispatches it to the remote site, returning the output from the remote endpoint.
The New Quest:
Bring WooCommerce to Calypso
“Stop! Who would cross the Bridge of Calypso must answer me these questions three, ere the other side he see.”
https://github.com/Automattic/wp-calypso
“I blow my nose at your extra whitespace!”
“Your mother was a sloppy coder, and your father smelt of space indenting!”
“Now fix your formatting, or I will taunt you a second time!"
React
Redux
Calypso Framework
Use Component State!
Single State Tree!
First, use Flux, then use Redux
Black Knight
of Webpack
Returns!
function generateReducerRequireString( extensionDir ) {
return `'${ camelCase( extensionDir ) }': require( 'extensions/${ extensionDir }/state/reducer' )`;
}
function generateExtensionsModuleString( reducerRequires ) {
return `module.exports = {
reducers: function() {
return {
${ reducerRequires.join( ',\n' ) }
};
}
};`;
}
Unit Tests are Important
"I like what you did with the exports particularly. But there is one small problem..."
// client/state/data-layer/extensions-middleware.js
const configuration = configureMiddleware( {}, {} );
export function configureMiddleware( handlers, config = configuration ) {
config.handlers = handlers;
config.middleware = buildMiddleware( handlers );
return config;
}
Add new extension
- client
- extensions
- woocommerce
- package.json
- index.js
// package.json
{
"name": "woocommerce",
"author": "Automattic",
"description": "WooCommerce Store",
"version": "0.0.1",
"env_id": [ "development", "wpcalypso" ],
"section": {
"name": "woocommerce",
"paths": [ "/store" ],
"module": "woocommerce",
"group": "sites",
"secondary": true
}
}
Add new extension
Hooking up to React
// index.js
renderWithReduxStore(
React.createElement( ProductCreate, { } ),
document.getElementById( 'primary' ),
context.store
);
Add new extension
Hooking up to React
Hooking up to Redux
// state/reducer.js
export default combineReducers( {
ui,
wcApi,
} );
Add new extension
Hooking up to React
Hooking up to Redux
Adding URL Routes
// index.js
export default function() {
if ( config.isEnabled( 'woocommerce/extension' ) ) {
page( '/store/:site?', siteSelection, navigation, Controller.dashboard );
page( '/store/:site?/products/add', siteSelection, navigation, Controller.addProduct );
}
}
Add new extension
Hooking up to React
Hooking up to Redux
Adding URL Routes
WP REST API (via Jetpack)
// state/wc-api/product-categories/actions.js
// In action creator...
return wp.req.get( { path: jpPath }, { path: apiPath } )
.then( ( { data } ) => {
dispatch( fetchProductCategoriesSuccess( siteId, data ) );
} )
.catch( err => {
dispatch( error( siteId, getAction, err ) );
} );
Add new extension
Hooking up to React
Hooking up to Redux
Adding URL Routes
WP REST API (via Jetpack)
i18n
// app/product/product-form-details-card.js
import i18n from 'i18n-calypso';
// In React Component class...
render() {
const { product, translate } = this.props;
return (
<Card className="products__product-form-details">
<FormLabel>{ translate( 'Featured' ) }</FormLabel>
</Card>
);
}
Add new extension
Hooking up to React
Hooking up to Redux
Adding URL Routes
WP REST API (via Jetpack)
i18n
Dependencies on WP Plugins
Dependencies on other Extensions
Upgrade path/version control
Capabilities are present, but constantly evolving.
Questions?