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 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. 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 /> <ReadMore list />

View file

@ -1,20 +1,53 @@
--- ---
title: Hooks title: Lifecycle hook methods
order: 60 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) ## Signature
- [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)
You can register a method for a hook. When the hook is triggered, your method will be To provide one or more hooks, your plugin should have a `hooks` property that
called. It will receive two parameters: 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) - 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 title: Macro methods
order: 90 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) ## Signature
- The context (`this`) of a macro method is **always** a [Part](/reference/api/part) object.
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 ```mjs
import {name, version} from '../package.json'; const myPlugin = {
name: 'example',
export default { version: '0.0.1',
name,
version,
macros: { macros: {
box: function(so) { example: function(so, { log }) {
this.points.boxTopLeft = so.anchor; log.info('Running the example macro')
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');
}
}
} }
``` ```
Did you figure out what this plugin does? ## Arguments
It provides a `box` macro that draws a box on our pattern in a given location with a give size.
We can use it like this: All macros receive two arguments:
```js - `so`: A plain object holding configuration object passed to the macro
points.boxAnchor = new Point(100, 100); - `props`: The same object as passed to the Part.draft()` method that you can destructure
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.
<Note> <Note>
###### Macros take only 1 argument ###### Macros take only 1 argument
When writing a macro, keep in mind that all information that needs to be passed 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. Typically, you use a single plain object to configure the macro.
</Note> </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-export-dxf**: DXF is kinda garbage, you deserve better
- **@freesewing/plugin-validate** - **@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 ## Migrating designs
### Design configuration ### Design configuration