All pages
Powered by GitBook
1 of 10

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Add-on Structure

Add-ons extend Local in one of two ways:

  • Events that are registered with the Local UI are added to the "Renderer" entry point.

  • Events that don't register with the Local UI are added to the "Main" entry point.

​Renderer entry point​

The renderer process is what runs inside Local's main window. This is where you'll add buttons, forms, and any other user interface elements.

Most Add-ons will exclusively use the renderer process entry point for the sake of simplicity.

To use this entry point, ensure that a renderer property is defined within the package.json file, which points to the compiled Javascript file.

Local passes a object to the exported function, which allows you to leverage existing tools that are already shipped with Local.

​Main entry point​

The main process allows you to register and hook into Local functionality that doesn't rely on the Local UI. By tapping into the main process, you can run processes even when the window is closed but Local is still running.

To use this entry point, ensure that a main property is defined within the packge.json file, which points to the compiled Javascript file.

Local passes a object to the exported function, which allows you to leverage existing tools that are already shipped with Local.

context
context

Building your Add-on

Local is powered by Electron/Node.js. This means all Add-ons interface with Local using a simple JavaScript API.

With that in mind, don't think Local Add-ons can only be JavaScript. Node.js provides robust APIs such as child_process which enable executing outside binaries and shell scripts.

Also, thanks to the vast ecosystem of modern JavaScript, you can write your Local Add-on in any language that transpiles to ES5, such as ES6, ES7, or TypeScript.

You can get up and running with your new add-on quickly and easily with the Local Add-on Generator. It is super simple to set up, and can help you start creating your new add-on in no time! It automatically sets up a bare-bones add-on in Local, and shows some helpful tips and examples (many of which are detailed in the following sections) to jumpstart your add-on development.

The Local Add-on API is always growing! If you need a hook to be added for any reason, please let us know in the and we'll be happy to work with you.

Local Community

Common Parameters

site

Plain JavaScript object containing information about the selected site.

Example:

siteStatus

String containing one of the following values:

Common statuses

  • adding

  • cloning

  • copying

  • deleting

Error statuses

  • container-missing

  • provisioning-error

  • wordpress-install-error

Pulling

  • pulling-downloading-backup

  • pulling-importing-backup

  • pulling-requesting-backup

Pushing

  • pushing-packing

  • pushing-processing

  • pushing-uploading

{
    "id": "BeyQOnaNS",
    "localVersion": "3.1.2",
    "container": "c61d2aba97ca2306edd42f03275dae0bbda570abe8d93e9ad2ba2a9a93bea895",
    "phpVersion": "7.2.9",
    "webServer": "nginx",
    "mysqlVersion": "5.7",
    "multiSite": null,
    "mysql": {
      "database": "local",
      "user": "root",
      "password": "root"
    },
    "ports": {
      "HTTP": 4049,
      "HTTPS": 4050,
      "MYSQL": 4051,
      "MAILCATCHER": 4052
    },
    "flywheelConnect": "flywheel-site-hash",
    "environment": "flywheel",
    "workspace": null,
    "name": "Example Site",
    "path": "/Users/username/Local Sites/example-site",
    "domain": "example-site.local",
    "environmentVersion": "1.3.2",
    "sslSHA1": "14f0808d7312bf4031ee198aec75cbd20b8aee2d"
  }

exporting

  • exporting-db

  • halted

  • provisioning

  • running

  • saving

  • stopping

  • Hooks

    The core of Local's Add-on API is the Hooks API. This API was heavily inspired by WordPress' Plugin API. If you've written WordPress themes or plugins before, you should feel right at home!

    Content Hooks

    Filters
    Actions

    Using local-components

    If you need to add UI components to your Local Add-on, we recommend using the @getflywheel/local-components package which is available on npm.

    To see what components are available in local-components, check out our styleguide.

    local-components utilizes CSS Modules, which allows multiple versions of local-components to be loaded into Local via multiple Add-ons. This allows for your Add-on to look and behave the same even if the user updates Local.

    Actions

    Available Actions

    Action Name

    Parameters

    changeSiteDomain

    oldDomain (string)

    newDomain (string)

    wordPressInstaller:standardInstall

    Unlike Content Hooks and Filters, Actions do not need to return a value.

    Example

    Context API

    context.environment

    Includes the following properties:

    • appPath: Path to running Local by Flywheel.app

    site

    wordPressInstaller:import

    site

    wordPressInstaller:import:includesWP

    site

    siteStarted

    siteprocesses

    siteStopped

    site

    siteCloned

    site

    siteAdded

    site

    siteDeleted

    Site id

    changeEnvironment:before

    site

    Plain object containing:

    • environment

    • webServer

    • phpVersion

    • mysqlVersion

    • clone (boolean)

    searchReplaceWPDatabase

    • site

    • oldDomain

    • newDomain

    • subSiteURLs

    site
    hooks.addAction('siteAdded', function (site) {
    	console.log('New site added!', site);
    });

    userHome: Path to current user home directory

  • phpVersion: Available PHP versions in Local

  • version: Local version

  • dockerPath: Path to Docker binary in Local by Flywheel.app

  • userDataPath: Path to Local's user data folder. On macOS this defaults to ~/Library/Application Support/Local by Flywheel

  • context.hooks

    Use context.hooks to run actions at certain times. Hooks are especially useful when adding in element with context.React.

    context.electron

    Exposes Electron API. The available methods and classes will differ based on whether or not this is ran from the main process or renderer process.

    With the Electron API you can create dialogs, open new windows, and more.

    context.fileSystem

    ​fs-extra npm package, which extends the native fs API in Node.js.

    context.fileSystemJetpack

    ​fs-jetpack npm package. Some may prefer it over the native fs API in Node.js.

    context.notifier

    ​node-notifier npm package. The main method in this class is notify.

    Example

    context.process

    Node.js process object.

    context.React

    Renderer Only

    context.React required to use JSX in your renderer entry point. You can also use context.React to access React's Top-Level API.

    context.ReactRouter

    • Link - https://reactrouter.com/web/api/Link

    • NavLink - https://reactrouter.com/web/api/NavLink

    • Route - https://reactrouter.com/web/api/Route

    • Router -

    Deprecated

    The following contexts have been deprecated in the most recent versions of Local.

    context.request

    ​request npm package, which makes it very easy to send HTTP/HTTPS requests.

    The request package was deprecated on Feb 11th 2020, and removed from Local in the [v8.0.0](https://community.localwp.com/t/local-beta-8-0-0/39385) release.

    context.docker

    Use context.docker to run Docker commands.

    Example

    context.notifier.notify({  
        title: 'Notification Title',  
        message: 'This is an example notification message.',
    });
    context.docker(`start ${container}`).then(stdout => {    
        //Success
    }).catch(({stdout, stderr}) => {    
        //Something bad happened
    });
    https://reactrouter.com/web/api/Router

    Filters

    Filters enable easily modifying data that is passed around in Local. Much like WordPress' filters, all filters in Local must return a value.

    Available Filters

    Filter Name

    appMenu

    Description

    Allows you to add or modify items in the main app menu (top bar menu)

    Callback Function Parameters

    • macOS Menu Template

    • Current Platform provided by process.platform

    _____________________________________________________________

    Filter Name

    siteInfoMoreMenu

    Description

    Allows you to add menu items to the Site Info More menu inside of a single site view.

    Callback Function Parameters

    • menuItems

    Example

    _____________________________________________________________

    Filter Name

    preferencesMenuItems

    Description

    Add-ons have the ability to add items to Local's preferences section by using the preferencesMenuItems filter hook. This filter will add a new item to the left hand column of all Preferences views and allow add-ons to add their own content section.

    An object is given to Local via the preferencesMenuItems hook (see usage examples below). This can then render an array of 's or any React component. The latter is provided mainly as an escape hatch in the case that an add-on needs more flexibility. Each PreferencesSection then provides an optional header and a single or array of (s).

    Props passed to <Row /> or <Override /> components

    Callback Function Parameters

    • preferencesMenu

    Examples

    "Standard" example passing in sections and sub-headers

    Overriding Sections with a single React Component (allows you to render arbitrary things in the "content area")

    Environment Filters

    ENV_ID and ENV_VERSION are for illustrative purposes only and should be replaced with a static value or variable.

    Content Hooks

    Content Hooks allow you to inject React components into various location in Local's UI. With Content Hooks you can do anything from adding additional buttons, table rows, or even new pages and routes.

    Available Content Hooks

    SiteInfoDatabase_TableList

    SiteInfoDatabase_TableList_TableListRow[Connect]:Before

    SiteInfoOverview

    SiteInfoOverview:Before

    SiteInfoOverview_TableList

    SiteInfoOverview_TableList:Before

    SiteInfo_TabNav_Items

    SiteInfo_Top_TopRight

    SitesSidebar_SitesSidebarSites:Before

    SitesSidebar_SitesSidebarSites

    SiteInfoSSL

    SiteInfoSSL_TableList

    SiteInfoUtilities

    SiteInfoUtilities_TableList

    routesRoot

    Accepts React Router <Route /> components

    routes[main]

    Accepts React Router <Route /> components

    routes[site-info]

    Accepts React Router <Route /> components

    Examples

    Adding a React Component (source)

    Content Hooks are added to an array of React Components. This means all components need to have a key prop.

    Adding a Route (source)

    Environment Content Hooks

    The following Content Hooks are meant to be used by Environments such as the Preferred and Custom Environment.

    ENV_ID and ENV_VERSION are for illustrative purposes only and should be replaced with a static value or variable.

    ENV_ID and ENV_VERSION are in the hook name to allow for multiple Environments to be loaded at the same time and to cut down on the number of conditionals required.

    Hook Name

    NewSiteEnvironment[ENV_ID][ENV_VERSION]_AddSiteContent_Inner

    SaveBlueprintPage[ENV_ID][ENV_VERSION]_SettingsPane_TableList

    SettingsNewSiteDefaults[ENV_ID][ENV_VERSION]

    SiteInfoEnvironmentMySQL[ENV_ID][ENV_VERSION]

    SiteInfoEnvironmentPHP[ENV_ID:ENV_VERSION]

    SiteInfoEnvironmentWebServer[ENV_ID][ENV_VERSION]

    Hook Name

    Parameters

    stylesheets

    SiteInfoDatabase

    Filter Name

    Parameters

    importBlueprintSiteSettings:ENV_ID:ENV_VERSION

    site

    currentSite
    AddonSettingsItem
    PreferencesSection
    MenuContentRowItem

    Example Add-ons

    Curious to see how this works in practice? Check out the Add-ons in the . Each add-on has a link to the Repository where you can review the code or clone it locally to pick apart and improve!

    hooks.addContent('SiteInfoOverview', (site) => {
        return (
            <Notes key="notes" site={site} />
        );
    });
    hooks.addContent('routesSiteInfo', () => {
        return (
            <Route 
                key="site-info-stats" 
                path="/site-info/:siteID/stats" 
                component={SiteInfoStats}
            />
        );
    });
    function(menu: MenuTemplate[], platform: string): MenuTemplate[];
    function(menuItems: SiteInfoMoreMenuItem[], site: Site): SiteInfoMoreMenuItem[];
    hooks.addFilter('siteInfoMoreMenu', function (menu, site) {
    
    	menu.push({
    		label: 'Volumes',
    		enabled: !this.context.router.isActive(`/site-info/${site.id}/volumes`),
    		click: () => {
    			context.events.send('goToRoute', `/site-info/${site.id}/volumes`);
    		},
    	});
    
    	return menu;
    
    });
    {
    	// React Router props (types are not accurately documented here and developers should reference the React Router documentation for more details)
    	history: any;
    	location: any;
    	match: any;
    	params: any;
    
    	// controls whether or not the "apply" button (rendered by Local) is enabled (clickable) or disabled (not clickable)
    	setApplyButtonDisabled: (isDisabled: boolean) => void;
    
    	// various other props may be passed via Local but are not an offically supported component of this API as of the time of writing
    }
    function(preferencesItem: PreferencesItem[]): PreferencesItem[];
    const preferenceItem: AddonSettingsItem = {
    	path: 'image-optimizer',
    	displayName: 'Image Optimizer',
    	sections: [
    		{
    			// subHeader is optional and if omitted, no subHeader will be rendered
    			subHeader: 'header 1',
    			rows: [
    				{
    					// this defines what string will be rendered in the left hand "column" of a row
    					name: 'line 1',
    					// this defines what will be rendered in the right hand "column" of a row. It can be any React component.
    					component: Preferences,
    				},
    				// ...add some more rows if you want
    			],
    		},
    		// ...add some more sections if you want
    	],
    	onApply: () => {
    		console.log('changes applied!')
    	},
    	componentProps: {
    		// This optional object can be any arbitrary props to get passed to your row components (or Override component)
    	},
    };
    
    hooks.addFilter(
    	'preferencesMenuItems',
    	(menu) => {
    		menu.push(preferenceItem);
    
    		// remember to return the menu array here! If you don't Local won't be able to render your Preference item
    		return menu;
    	}
    );
    const preferenceItem = {
    	path: 'image-optimizer',
    	displayName: 'Image Optimizer',
    	// this can also just be any old React component. It will be rendered in the Preferences content area
    	sections: (props) => <div>{props.title}<div/>,
    	onApply: () => {
    		console.log('changes applied!')
    	},
    	componentProps: {
    		/* Arbitrary props that will get passed to your row components or section override component. */
    		/* here we are passing it "title" which will get passed to the "sections" component */
    		title: 'Preferences Title',
    	},
    }
    
    hooks.addFilter(
    	'preferencesMenuItems',
    	(menu) => {
    		menu.push(preferenceItem);
     
    		return menu;
    	};
    );
    site
    site
    site
    site
    site
    site
    site
    site
    site
    siteStatus
    site
    site
    site
    site
    Add-on Library