1
0
Fork 0

chore(markdown): Updated plugin guide for v3

This commit is contained in:
Joost De Cock 2022-10-12 21:52:47 +02:00
parent d7442b9bc4
commit cac698027c
15 changed files with 195 additions and 348 deletions

View file

@ -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.

View file

@ -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 />

View file

@ -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.

View file

@ -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] )
```

View file

@ -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>

View 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.

View file

@ -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.

View file

@ -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.

View 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.

View 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.

View file

@ -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.

View file

@ -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
]
}
}
```

View file

@ -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.

View file

@ -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>

View file

@ -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