chore(markdown): Updated plugin guide for v3
This commit is contained in:
parent
d7442b9bc4
commit
cac698027c
15 changed files with 195 additions and 348 deletions
|
@ -1,65 +0,0 @@
|
|||
---
|
||||
title: Conditionally loading build-time plugins
|
||||
order: 30
|
||||
---
|
||||
|
||||
You can choose to load your build-time plugin conditionally based on run-time data.
|
||||
|
||||
To do so, you need to create a `condition` method that will determine whether the
|
||||
plugin will be loaded. This method receives the complete settings object and should
|
||||
return `true` if the plugin is to be loaded, and `false` if it should not be loaded.
|
||||
|
||||
```js
|
||||
const condition = settings => {
|
||||
if (settings) {
|
||||
// Remember, settings contains:
|
||||
// settings.options => The user's options
|
||||
// settings.measurements => The measurements
|
||||
return true // Load the plugin
|
||||
}
|
||||
else return false // Do not load the plugin
|
||||
}
|
||||
```
|
||||
|
||||
You pass your plugin and condition method as a third parameter to the Design constructor
|
||||
with the `plugin` and `condition` keys respectively.
|
||||
|
||||
Let's look at a complete example to illustrate this:
|
||||
|
||||
```js
|
||||
import freesewing from '@freesewing/core'
|
||||
import plugins from '@freesewing/plugin-bundle'
|
||||
import myConditionalPlugin from '@freesewing/plugin-bust'
|
||||
|
||||
const myConditionalPluginCheck = (settings = false) =>
|
||||
settings &&
|
||||
settings.options &&
|
||||
settings.options.draftForHighBust &&
|
||||
settings.measurements.highBust
|
||||
? true
|
||||
: false
|
||||
|
||||
const Pattern = new freesewing.Design(
|
||||
config,
|
||||
plugins,
|
||||
{
|
||||
plugin: myConditionalPlugin,
|
||||
condition: myConditionalPluginCheck
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
Our condition method will return `true` only if the following conditions are met:
|
||||
|
||||
- A `settings` object is passed into the method
|
||||
- `settings.options` is _truthy_
|
||||
- `settings.options.draftForHighBust` is _truthy_
|
||||
- `settings.options.measurements.highBust` is _truthy_
|
||||
|
||||
This is a real-world example from our Teagan pattern. A t-shirt pattern that can be
|
||||
drafted to the high bust (rather than the full chest circumference) if the user
|
||||
choses so.
|
||||
|
||||
But that feat is handled auto-magically by `plugin-bust` which is a build-time plugin.
|
||||
So whether to load this plugin or not hinges on the user settings, which is why we
|
||||
load this plugin conditionally.
|
|
@ -1,25 +1,23 @@
|
|||
---
|
||||
title: Plugin guide
|
||||
order: 400
|
||||
icons:
|
||||
- logo
|
||||
- plugin
|
||||
for: developers
|
||||
about: |
|
||||
This guide shows you everything you need to know to understand plugins in FreeSewing, and create your own.
|
||||
goals:
|
||||
- Know about build-time plugins vs run-time plugins
|
||||
- Understanding plugin structure
|
||||
- Hooks and how to use them
|
||||
- Using hooks without a plugin
|
||||
- Using macros
|
||||
---
|
||||
|
||||
Plugins allow you to extend FreeSewing.
|
||||
Plugins allow you to extend FreeSewing with new features and functionality.
|
||||
A FreeSewing plugin can extend FreeSewing in 3 different ways:
|
||||
|
||||
We have [a list of available plugins](/reference/plugins/), but
|
||||
- It can [provide macros](/guides/plugins/macros), which are a way to automate a number of steps into a
|
||||
single command.
|
||||
- It can [hook into the pattern](/guides/plugins/hooks), which allows you to manipulate the pattern or
|
||||
interact with it at various stages of it's lifecycle.
|
||||
- It can [provide store methods](/guides/plugins/store), which allows you to add new ways to handle data
|
||||
in the pattern, including providing a custom logger.
|
||||
|
||||
We have [a list of plugins](/reference/plugins/) that we maintain, but
|
||||
if you can't find what you're looking for, you can write your own plugin.
|
||||
|
||||
We'll cover the following topics in this guide:
|
||||
If you plan on doing that, or if you would like to understand how plugins work,
|
||||
this guide is for you.
|
||||
|
||||
We'll cover the following topics:
|
||||
|
||||
<ReadMore list />
|
||||
|
|
|
@ -1,20 +1,53 @@
|
|||
---
|
||||
title: Hooks
|
||||
order: 60
|
||||
title: Lifecycle hook methods
|
||||
order: 110
|
||||
---
|
||||
|
||||
A **hook** is a lifecycle event. The available hooks are:
|
||||
FreeSewing plugins can provide hooks, which is a way to hook into the pattern's
|
||||
lifecycle.
|
||||
|
||||
- [preRender](/reference/hooks/prerender/): Called at the start of [`Pattern.render()`](/reference/api/pattern/render)
|
||||
- [postRender](/reference/hooks/postrender/): Called at the end of [`Pattern.render()`](/reference/api/pattern/render)
|
||||
- [insertText](/reference/hooks/inserttext/): Called when inserting text
|
||||
- [preDraft](/reference/hooks/predraft/): Called at the start of [`Pattern.draft()`](/reference/api/pattern/draft)
|
||||
- [postDraft](/reference/hooks/postdraft/): Called at the end of [`Pattern.draft()`](/reference/api/pattern/draft)
|
||||
- [preSample](/reference/hooks/presample/): Called at the start of [`Pattern.sample()`](/reference/api/pattern/sample)
|
||||
- [postSample](/reference/hooks/postsample/): Called at the end of [`Pattern.sample()`](/reference/api/pattern/sample)
|
||||
## Signature
|
||||
|
||||
You can register a method for a hook. When the hook is triggered, your method will be
|
||||
called. It will receive two parameters:
|
||||
To provide one or more hooks, your plugin should have a `hooks` property that
|
||||
is an object where the keys are the lifecycle hook name, and the value holds a
|
||||
method. When the lifecycle hook is triggered, your method will be called.
|
||||
|
||||
- An object relevant to the hook. See the [hooks API reference](/reference/hooks/) for details.
|
||||
```mjs
|
||||
const myPlugin = {
|
||||
name: 'example',
|
||||
version: '0.0.1',
|
||||
hooks: {
|
||||
hookName: function (obj, data = {}) {
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you want to attach multiple methods to the same lifecycle hook, you can pass
|
||||
them as an array:
|
||||
|
||||
```mjs
|
||||
const myPlugin = {
|
||||
name: 'example',
|
||||
version: '0.0.1',
|
||||
hooks: {
|
||||
hookName: [
|
||||
function one (obj, data = {}) { },
|
||||
function two (obj, data = {}) { }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Arguments
|
||||
|
||||
All lifecycle methods will receive two parameters:
|
||||
|
||||
- An object relevant to the lifecycle hook. See the [hooks API reference](/reference/hooks/) for details.
|
||||
- Data passed when the hook was registered (optional)
|
||||
|
||||
## Notes
|
||||
|
||||
Refer to the [hooks API reference](/reference/hooks/) for a list of all
|
||||
available lifecycle hooks.
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
---
|
||||
title: Loading build-time plugins
|
||||
order: 20
|
||||
---
|
||||
|
||||
Build-time plugins are loaded at build time, by passing them to
|
||||
the [`freesewing.Design`](/reference/api/design) constructor:
|
||||
|
||||
```js
|
||||
import freesewing from "@freesewing/core"
|
||||
import plugins from "@freesewing/plugin-bundle"
|
||||
import config from "../config"
|
||||
|
||||
const Pattern = new freesewing.Design(config, plugins)
|
||||
```
|
||||
|
||||
If you have multiple plugins to load, you can pass them as an array:
|
||||
|
||||
```js
|
||||
import freesewing from "@freesewing/core"
|
||||
import plugins from "@freesewing/plugin-bundle"
|
||||
import gorePlugin from "@freesewing/plugin-gore"
|
||||
import config from "../config"
|
||||
|
||||
const Pattern = new freesewing.Design(config, [plugins, gorePlugin] )
|
||||
```
|
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
title: Loading run-time plugins
|
||||
order: 40
|
||||
---
|
||||
|
||||
Run-time plugin are loaded at run time, by passing them to the `use` method of
|
||||
an instatiated pattern. That method is chainable, so if you have multiple plugins
|
||||
you can just chain them together:
|
||||
|
||||
```js
|
||||
import Aaron from "@freesewing/aaron";
|
||||
import theme from "@freesewing/plugin-theme";
|
||||
import i18n from "@freesewing/plugin-i18n";
|
||||
|
||||
const myAaron = new Aaron()
|
||||
.use(theme)
|
||||
.use(i18n)
|
||||
```
|
||||
|
||||
<Tip>
|
||||
|
||||
Plugins that use only hooks are typically run-time plugins
|
||||
|
||||
</Tip>
|
12
markdown/dev/guides/plugins/loading/en.md
Normal file
12
markdown/dev/guides/plugins/loading/en.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
title: Loading plugins
|
||||
order: 140
|
||||
---
|
||||
|
||||
Plugins can be loaded at build time and added to the desig. Or at run time and added to an instantiated pattern.
|
||||
|
||||
To load a plugin at build time, it should be added to [the `plugins` key of the part configuration](/reference/api/part/config/plugins).
|
||||
|
||||
To load a plugin at run time, it should be loaded with a call to [`Pattern.use()`](/reference/api/pattern/use).
|
||||
|
||||
Please refer to the relevant documentation for more details.
|
|
@ -1,58 +1,37 @@
|
|||
---
|
||||
title: Macros
|
||||
order: 90
|
||||
title: Macro methods
|
||||
order: 120
|
||||
---
|
||||
|
||||
Plugin structure for macros is similar, with a few changes:
|
||||
FreeSewing plugins can provide macros, which is a way to automate multiple
|
||||
steps into a single command.
|
||||
|
||||
- Rather than the hook name, you provide the macro name (that you choose yourself)
|
||||
- The context (`this`) of a macro method is **always** a [Part](/reference/api/part) object.
|
||||
## Signature
|
||||
|
||||
Apart from these, the structure is very similar:
|
||||
To provide one or more macros, your plugin should have a `macros` property that
|
||||
is an object where the keys are the macro name, and the value holds a method to
|
||||
run when the macro is executed.
|
||||
|
||||
```js
|
||||
import {name, version} from '../package.json';
|
||||
|
||||
export default {
|
||||
name,
|
||||
version,
|
||||
```mjs
|
||||
const myPlugin = {
|
||||
name: 'example',
|
||||
version: '0.0.1',
|
||||
macros: {
|
||||
box: function(so) {
|
||||
this.points.boxTopLeft = so.anchor;
|
||||
this.points.boxTopRight = so.anchor.shift(0, so.size);
|
||||
this.points.boxBottomRight = this.points.boxTopRight.shift(-90, so.size);
|
||||
this.points.boxBottomLeft = new this.Point(so.anchor.x, this.points.boxBottomRight.y);
|
||||
|
||||
this.paths.box = new this.Path()
|
||||
.move(this.points.boxTopLeft)
|
||||
.line(this.points.boxTopRight)
|
||||
.line(this.points.boxBottomRight)
|
||||
.line(this.points.boxBottomLeft)
|
||||
.close()
|
||||
.attr('class', 'box');
|
||||
}
|
||||
}
|
||||
example: function(so, { log }) {
|
||||
log.info('Running the example macro')
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Did you figure out what this plugin does?
|
||||
It provides a `box` macro that draws a box on our pattern in a given location with a give size.
|
||||
## Arguments
|
||||
|
||||
We can use it like this:
|
||||
All macros receive two arguments:
|
||||
|
||||
```js
|
||||
points.boxAnchor = new Point(100, 100);
|
||||
macro('box', {
|
||||
anchor: points.boxAnchor
|
||||
size: 25
|
||||
});
|
||||
```
|
||||
|
||||
Obviously, you can expect to learn how to call a macro in its documentation,
|
||||
rather than have to comb through its code.
|
||||
- `so`: A plain object holding configuration object passed to the macro
|
||||
- `props`: The same object as passed to the Part.draft()` method that you can destructure
|
||||
|
||||
<Note>
|
||||
|
||||
###### Macros take only 1 argument
|
||||
|
||||
When writing a macro, keep in mind that all information that needs to be passed
|
||||
|
@ -61,3 +40,7 @@ to a macro needs to be contained in a single argument.
|
|||
Typically, you use a single plain object to configure the macro.
|
||||
|
||||
</Note>
|
||||
|
||||
## Return value
|
||||
|
||||
Macros do not need to return anything. If they do, it will be ignored.
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
title: Plugin structure
|
||||
order: 50
|
||||
---
|
||||
|
||||
Plugins can do two things:
|
||||
|
||||
- They can use hooks
|
||||
- They can provide macros
|
||||
|
||||
Your plugin should export an object with the following structure:
|
||||
|
||||
```js
|
||||
{
|
||||
name: 'myPlugin',
|
||||
version: '1.0.0',
|
||||
hooks: {},
|
||||
macros: {}
|
||||
};
|
||||
```
|
||||
|
||||
The `name` and `version` attributes are self-explanatory.
|
||||
The [hooks](/guides/plugins/hooks/) and [macros](/guides/plugins/macros/) sections
|
||||
explain the `hooks` and `macros` properties.
|
56
markdown/dev/guides/plugins/store/en.md
Normal file
56
markdown/dev/guides/plugins/store/en.md
Normal file
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
title: Store methods
|
||||
order: 130
|
||||
---
|
||||
|
||||
FreeSewing plugins can provide store methods, which facilitate data handling
|
||||
within a pattern.
|
||||
|
||||
## Signature
|
||||
|
||||
To provide one or more store methods, your plugin should have a `macros` property that
|
||||
is an array where each member is itself an array with two members:
|
||||
|
||||
- The first member holds the key to attach the method to (in dot notation)
|
||||
- The second member holds the method to attach
|
||||
|
||||
```mjs
|
||||
const myPlugin = {
|
||||
name: 'example',
|
||||
version: '0.0.1',
|
||||
store: [
|
||||
[
|
||||
'log.panic',
|
||||
function(store, ...params) {
|
||||
store.setIfUnset('logs.panic', new Array())
|
||||
store.push(...params)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Arguments
|
||||
|
||||
All store methods receive at least two arguments:
|
||||
|
||||
- `store`: The store object itself
|
||||
- `...params`: All additional plugins that were passed to the store method
|
||||
|
||||
## Overwriting store methods
|
||||
|
||||
You are allowed to overwrite existing store methods.
|
||||
As it happens, this is how you should implement a custom logging solution, but overwriting the logging methods under the store's `log` key,
|
||||
|
||||
However, the following methods cannot be overwritten:
|
||||
|
||||
- `extend`
|
||||
- `get`
|
||||
- `push`
|
||||
- `set`
|
||||
- `setIfUnset`
|
||||
- `unset`
|
||||
|
||||
## Return value
|
||||
|
||||
Store methods do not need to return anything. If they do, it will be ignored.
|
25
markdown/dev/guides/plugins/structure/en.md
Normal file
25
markdown/dev/guides/plugins/structure/en.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
title: Plugin structure
|
||||
order: 100
|
||||
---
|
||||
|
||||
A FreeSewing plugin is a plain object with the following structure:
|
||||
|
||||
```mjs
|
||||
Object plugin = {
|
||||
String name,
|
||||
String version,
|
||||
Object hooks,
|
||||
Object macros,
|
||||
Array store,
|
||||
}
|
||||
```
|
||||
|
||||
A plugin **must** have the `name` and `version` properties.
|
||||
The other properties are optional, and they map to the three different functionalities macros can provide:
|
||||
|
||||
- `hooks`: Holds an object with lifecycle hooks the plugin wants to hook into
|
||||
- `macros`: Holds and object with macros the plugin provides
|
||||
- `store`: Holds and Array with store methods the plugin provides.
|
||||
|
||||
Click on the links above for more details on the structure of these properties.
|
|
@ -1,36 +0,0 @@
|
|||
---
|
||||
title: Types of plugins
|
||||
order: 10
|
||||
---
|
||||
|
||||
Plugins come in two flavours:
|
||||
|
||||
- [Build-time plugins](#build-time-plugins)
|
||||
- [Run-time plugins](#run-time-plugins)
|
||||
|
||||
When writing a plugin, ask yourself whether it's a run-time or a build-time plugin.
|
||||
And if the answer is both, please split them into two plugins.
|
||||
|
||||
## Build-time plugins
|
||||
|
||||
A plugin is a build-time plugin if it is required by the pattern at build-time.
|
||||
In other words, the plugin is a dependency for the pattern, and if it's missing
|
||||
the pattern won't load.
|
||||
|
||||
<Tip>
|
||||
|
||||
Our [plugin bundle](/reference/plugins/bundle/) bundles build-time plugins that are used in many patterns.
|
||||
|
||||
</Tip>
|
||||
|
||||
<Note>Plugins that provide a macro are typically build-time plugins</Note>
|
||||
|
||||
## Run-time plugins
|
||||
|
||||
A plugin is a run-time plugin if it can be added after instantiating your pattern.
|
||||
Think of it as a plugin to be used in the front-end.
|
||||
|
||||
Run-time plugins are not a dependecy of the pattern. They just _add something_ to it.
|
||||
|
||||
Our [theme plugin](/reference/plugins/theme/) is a good example of a run-time plugin.
|
||||
If it's missing, your pattern will still work, it just won't look pretty.
|
|
@ -1,26 +0,0 @@
|
|||
---
|
||||
title: Using hooks more than once
|
||||
order: 80
|
||||
---
|
||||
|
||||
What if you want to attach more than one method to a hook?
|
||||
You could spread them over separate plugins, but there's a better way.
|
||||
|
||||
Rather than assigning a method to your hook, assign an array of methods like this:
|
||||
|
||||
```js
|
||||
import myCoolMethod from './method-a';
|
||||
import myEvenCoolerMethod from './method-b';
|
||||
import {name, version} from '../package.json';
|
||||
|
||||
export default {
|
||||
name,
|
||||
version,
|
||||
hooks: {
|
||||
preRender: [
|
||||
myCoolMethod,
|
||||
myEvenCoolerMethod
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
|
@ -1,19 +0,0 @@
|
|||
---
|
||||
title: Using hooks without a plugin
|
||||
order: 85
|
||||
---
|
||||
|
||||
You can attach a method to a hook at run-time without the need for a plugin
|
||||
using the [Pattern.on()](/reference/api/pattern/on) method.
|
||||
|
||||
The method takes the hook name as its first argument, and the hook method as its second.
|
||||
|
||||
Below is an example:
|
||||
|
||||
```js
|
||||
pattern.on('preRender', function(svg) {
|
||||
svg.style += "svg { background: yellow;}";
|
||||
});
|
||||
```
|
||||
|
||||
Congratulations, you've just made your pattern yellow.
|
|
@ -1,58 +0,0 @@
|
|||
---
|
||||
title: Using hooks
|
||||
order: 70
|
||||
---
|
||||
|
||||
For each hook, your plugin should provide a method that takes the relevant data
|
||||
as its first argument. If data was passed when the hook was loaded, you will receive
|
||||
that as the second object.
|
||||
|
||||
Remember that:
|
||||
|
||||
- The `insertText` hook will receive a locale and string and you should return a string.
|
||||
- All other hooks receive an object. You don't need to return anything, but rather modify the object you receive.
|
||||
|
||||
Let's look at an example:
|
||||
|
||||
```js
|
||||
import myStyle from './style';
|
||||
import myDefs from './defs';
|
||||
import {name, version} from '../package.json';
|
||||
|
||||
export default {
|
||||
name,
|
||||
version,
|
||||
hooks: {
|
||||
preRender: function(svg) {
|
||||
if (svg.attributes.get("freesewing:plugin-"+name) === false) {
|
||||
svg.style += myStyle;
|
||||
svg.defs += myDefs;
|
||||
svg.attributes.add("freesewing:plugin-"+name, version);
|
||||
}
|
||||
},
|
||||
insertText: function(text) {
|
||||
return text.toUpperCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is a complete plugin, ready to be published on NPM. It uses two hooks:
|
||||
|
||||
- `preRender` : We add some style and defs to our SVG
|
||||
- `insertText` : We transfer all text to UPPERCASE
|
||||
|
||||
<Note>
|
||||
|
||||
###### Note that we avoid running our hook twice
|
||||
|
||||
As you can see, the last thing we do in the `preRender` hook is set an attribute on
|
||||
the SVG tag with the name and version of our plugin.
|
||||
|
||||
We check for this attribute when the `preRender` hook runs, thereby avoiding that
|
||||
our styles and defs will be added twice.
|
||||
|
||||
It is good practice to wrap you hook methods in a call like this, because you have
|
||||
no guarantee the user won't render your pattern more than once.
|
||||
|
||||
</Note>
|
|
@ -47,6 +47,24 @@ The following packages have been removed in v3:
|
|||
- **@freesewing/plugin-export-dxf**: DXF is kinda garbage, you deserve better
|
||||
- **@freesewing/plugin-validate**
|
||||
|
||||
### API changes
|
||||
|
||||
#### Use log instead of raise
|
||||
|
||||
The `raise` object that held methods for logging has been replaced by log:
|
||||
|
||||
```mjs
|
||||
// strikeout-start
|
||||
raise.warning('This raise object no longer exists')
|
||||
// strikeout-end
|
||||
// highlight-start
|
||||
raise.info('Use the log object instead')
|
||||
// highlight-end
|
||||
```
|
||||
|
||||
Note that `log` can be destructured in your draft method.
|
||||
Refer to [the `Store.log` documentation](/reference/api/store/log) for all details.
|
||||
|
||||
## Migrating designs
|
||||
|
||||
### Design configuration
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue