wip(core): Support for 2022 style Design constructor
This is the first commit to tackle some exploratory work in the context of discussion #2538 that deals with a number of things, such as: - Making it easier to attach parts to designs - Making it easier to attach parts at run-time - Simplify part inheritance into other designs - Find ways to handle dependenices across designs - Find ways to keep the part-specific config with the part In this initial commit, I've update the Design constructor to handle two different ways of calling it: - legacy: new Design(config, plugins, conditionalPlugins) - 2022: new Design(config) I didn't want to call this the `new` way because that doesn't age well, so I went with `legacy` and `2022` and this is how I will refer to them from now on. This is very much a work in progress and while I will create a PR to keep on eye on the tests, I don't expect to merge this as-is.
This commit is contained in:
parent
6e2a0a33d9
commit
ac9b616b99
4 changed files with 207 additions and 27 deletions
|
@ -1,29 +1,38 @@
|
||||||
import Pattern from './pattern'
|
import Pattern from './pattern'
|
||||||
|
|
||||||
// Default hide method for options
|
/*
|
||||||
const hide = () => false
|
* The Design constructor. Returns a Pattern constructor
|
||||||
|
* So it's sort of a super-constructor
|
||||||
|
*/
|
||||||
export default function Design(config, plugins = false, conditionalPlugins = false) {
|
export default function Design(config, plugins = false, conditionalPlugins = false) {
|
||||||
// Add default hide() method to config.options
|
|
||||||
for (const option in config.options) {
|
// Ensure all options have a hide() method
|
||||||
if (typeof config.options[option] === 'object') {
|
config.options = optionsWithHide(config.options)
|
||||||
config.options[option] = {
|
|
||||||
hide,
|
// A place to store deprecation and other warnings before we even have a pattern instantiated
|
||||||
...config.options[option],
|
config.warnings = []
|
||||||
}
|
|
||||||
}
|
/*
|
||||||
}
|
* The newer way to initalize a design is to pass one single parameter
|
||||||
|
* The old way passed multiple parameters.
|
||||||
|
* So let's figure out which is which and be backwards compatible
|
||||||
|
*
|
||||||
|
* This mitigation should be removed in v3 when we drop support for the legacy way
|
||||||
|
*/
|
||||||
|
config = migrateConfig(config, plugins, conditionalPlugins)
|
||||||
|
|
||||||
const pattern = function (settings) {
|
const pattern = function (settings) {
|
||||||
Pattern.call(this, config)
|
Pattern.call(this, config)
|
||||||
|
|
||||||
// Load plugins
|
// Load plugins
|
||||||
if (Array.isArray(plugins)) for (let plugin of plugins) this.use(plugin)
|
if (Array.isArray(config.plugins)) for (const plugin of config.plugins) this.use(plugin)
|
||||||
else if (plugins) this.use(plugins)
|
else if (config.plugins) this.use(config.plugins)
|
||||||
|
|
||||||
// Load conditional plugins
|
// Load conditional plugins
|
||||||
if (Array.isArray(conditionalPlugins))
|
if (Array.isArray(config.conditionalPlugins))
|
||||||
for (let plugin of conditionalPlugins) this.useIf(plugin, settings)
|
for (const plugin of config.conditionalPlugins) this.useIf(plugin, settings)
|
||||||
else if (conditionalPlugins.plugin && conditionalPlugins.condition)
|
else if (config.conditionalPlugins.plugin && config.conditionalPlugins.condition)
|
||||||
this.useIf(conditionalPlugins, settings)
|
this.useIf(config.conditionalPlugins, settings)
|
||||||
|
|
||||||
this.apply(settings)
|
this.apply(settings)
|
||||||
|
|
||||||
|
@ -39,3 +48,59 @@ export default function Design(config, plugins = false, conditionalPlugins = fal
|
||||||
|
|
||||||
return pattern
|
return pattern
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper method to handle the legacy way of passing configuration
|
||||||
|
* to the design constructor
|
||||||
|
*/
|
||||||
|
const migrateConfig = (config, plugins, conditionalPlugins) => {
|
||||||
|
|
||||||
|
// Migrate plugins
|
||||||
|
if (plugins && config.plugins) config.warnings.push(
|
||||||
|
'Passing plugins to the Design constructor both as a second parameter and in the config is unsupported',
|
||||||
|
'Ignoring plugins passed as parameter. Only config.plugins will be used.'
|
||||||
|
)
|
||||||
|
else if (plugins && !config.plugins) {
|
||||||
|
config.plugins = plugins
|
||||||
|
config.warnings.push(
|
||||||
|
'Passing a plugins parameter to the Design constructure is deprecated',
|
||||||
|
'Please store them in the `plugins` key of the config object that is the first parameter'
|
||||||
|
)
|
||||||
|
} else if (!config.plugins) config.plugins = []
|
||||||
|
|
||||||
|
// Migrate conditional plugins
|
||||||
|
if (conditionalPlugins && config.conditionalPlugins) config.warnings.push(
|
||||||
|
'Passing conditionalPlugins to the Design constructor both as a third parameter and in the config is unsupported.',
|
||||||
|
'Ignoring conditionalPlugins passes as parameter. Only config.conditionalPlugins will be used.',
|
||||||
|
)
|
||||||
|
else if (conditionalPlugins && !config.conditionalPlugins) {
|
||||||
|
config.conditionalPlugins = conditionalPlugins
|
||||||
|
config.warnings.push(
|
||||||
|
'Passing a conditionalPlugins parameter to the Design constructure is deprecated.',
|
||||||
|
'Please store them in the `conditionalPlugins` key of the config object that is the first parameter'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else if (!config.conditionalPlugins) config.conditionalPlugins = []
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A default hide() method for options that lack it
|
||||||
|
* Since this will always return false, the option will never be hidden
|
||||||
|
*/
|
||||||
|
const hide = () => false // The default hide() method
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper method to add the default hide() method to options who lack one
|
||||||
|
*/
|
||||||
|
const optionsWithHide = options => {
|
||||||
|
if (options) {
|
||||||
|
for (const option in options) {
|
||||||
|
if (typeof options[option] === 'object') options[option] = { hide, ...options[option] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@ import Attributes from './attributes'
|
||||||
import pkg from '../package.json'
|
import pkg from '../package.json'
|
||||||
|
|
||||||
export default function Pattern(config = { options: {} }) {
|
export default function Pattern(config = { options: {} }) {
|
||||||
// Default settings
|
|
||||||
|
// Apply default settings
|
||||||
this.settings = {
|
this.settings = {
|
||||||
complete: true,
|
complete: true,
|
||||||
idPrefix: 'fs-',
|
idPrefix: 'fs-',
|
||||||
|
@ -25,15 +26,16 @@ export default function Pattern(config = { options: {} }) {
|
||||||
absoluteOptions: {},
|
absoluteOptions: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Events store and raise methods
|
// Object to hold events
|
||||||
this.events = {
|
this.events = {
|
||||||
info: [],
|
info: [],
|
||||||
warning: [],
|
warning: [],
|
||||||
error: [],
|
error: [],
|
||||||
debug: [],
|
debug: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Raise methods - Make events and settings avialable in them
|
||||||
const events = this.events
|
const events = this.events
|
||||||
// Make settings available in the raise.debug method
|
|
||||||
const settings = this.settings
|
const settings = this.settings
|
||||||
this.raise = {
|
this.raise = {
|
||||||
info: function (data) {
|
info: function (data) {
|
||||||
|
@ -50,10 +52,13 @@ export default function Pattern(config = { options: {} }) {
|
||||||
if (settings.debug) events.debug.push(data)
|
if (settings.debug) events.debug.push(data)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Say hi
|
||||||
this.raise.info(
|
this.raise.info(
|
||||||
`New \`@freesewing/${config.name}:${config.version}\` pattern using \`@freesewing/core:${pkg.version}\``
|
`New \`@freesewing/${config.name}:${config.version}\` pattern using \`@freesewing/core:${pkg.version}\``
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// More things that go in a pattern
|
||||||
this.config = config // Pattern configuration
|
this.config = config // Pattern configuration
|
||||||
this.width = 0 // Will be set after render
|
this.width = 0 // Will be set after render
|
||||||
this.height = 0 // Will be set after render
|
this.height = 0 // Will be set after render
|
||||||
|
|
|
@ -21,7 +21,7 @@ it("Design constructor should return pattern constructor", () => {
|
||||||
expect(pattern.settings.options.percentage).to.equal(0.3);
|
expect(pattern.settings.options.percentage).to.equal(0.3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Design constructor should load single plugin", () => {
|
it("Design constructor should load single plugin (legacy)", () => {
|
||||||
let plugin = {
|
let plugin = {
|
||||||
name: "example",
|
name: "example",
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -37,7 +37,23 @@ it("Design constructor should load single plugin", () => {
|
||||||
expect(pattern.hooks.preRender.length).to.equal(1);
|
expect(pattern.hooks.preRender.length).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Design constructor should load array of plugins", () => {
|
it("Design constructor should load single plugin (2022)", () => {
|
||||||
|
let plugin = {
|
||||||
|
name: "example",
|
||||||
|
version: 1,
|
||||||
|
hooks: {
|
||||||
|
preRender: function(svg, attributes) {
|
||||||
|
svg.attributes.add("freesewing:plugin-example", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let design = new freesewing.Design({plugins: plugin});
|
||||||
|
let pattern = new design();
|
||||||
|
expect(pattern.hooks.preRender.length).to.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Design constructor should load array of plugins (legacy)", () => {
|
||||||
let plugin1 = {
|
let plugin1 = {
|
||||||
name: "example1",
|
name: "example1",
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -62,7 +78,32 @@ it("Design constructor should load array of plugins", () => {
|
||||||
expect(pattern.hooks.preRender.length).to.equal(2);
|
expect(pattern.hooks.preRender.length).to.equal(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Design constructor should load conditional plugin", () => {
|
it("Design constructor should load array of plugins (2022)", () => {
|
||||||
|
let plugin1 = {
|
||||||
|
name: "example1",
|
||||||
|
version: 1,
|
||||||
|
hooks: {
|
||||||
|
preRender: function(svg, attributes) {
|
||||||
|
svg.attributes.add("freesewing:plugin-example1", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let plugin2 = {
|
||||||
|
name: "example2",
|
||||||
|
version: 2,
|
||||||
|
hooks: {
|
||||||
|
preRender: function(svg, attributes) {
|
||||||
|
svg.attributes.add("freesewing:plugin-example2", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let design = new freesewing.Design( { plugins: [plugin1, plugin2] });
|
||||||
|
let pattern = new design();
|
||||||
|
expect(pattern.hooks.preRender.length).to.equal(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Design constructor should load conditional plugin (legacy)", () => {
|
||||||
const plugin = {
|
const plugin = {
|
||||||
name: "example",
|
name: "example",
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -78,7 +119,23 @@ it("Design constructor should load conditional plugin", () => {
|
||||||
expect(pattern.hooks.preRender.length).to.equal(1);
|
expect(pattern.hooks.preRender.length).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Design constructor should not load conditional plugin", () => {
|
it("Design constructor should load conditional plugin (2022)", () => {
|
||||||
|
const plugin = {
|
||||||
|
name: "example",
|
||||||
|
version: 1,
|
||||||
|
hooks: {
|
||||||
|
preRender: function(svg, attributes) {
|
||||||
|
svg.attributes.add("freesewing:plugin-example", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const condition = () => true
|
||||||
|
const design = new freesewing.Design({ conditionalPlugins: { plugin, condition } });
|
||||||
|
const pattern = new design();
|
||||||
|
expect(pattern.hooks.preRender.length).to.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Design constructor should not load conditional plugin (legacy)", () => {
|
||||||
const plugin = {
|
const plugin = {
|
||||||
name: "example",
|
name: "example",
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -94,7 +151,23 @@ it("Design constructor should not load conditional plugin", () => {
|
||||||
expect(pattern.hooks.preRender.length).to.equal(0);
|
expect(pattern.hooks.preRender.length).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Design constructor should load multiple conditional plugins", () => {
|
it("Design constructor should not load conditional plugin (2022)", () => {
|
||||||
|
const plugin = {
|
||||||
|
name: "example",
|
||||||
|
version: 1,
|
||||||
|
hooks: {
|
||||||
|
preRender: function(svg, attributes) {
|
||||||
|
svg.attributes.add("freesewing:plugin-example", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const condition = () => false
|
||||||
|
const design = new freesewing.Design({ conditionalPlugins: { plugin, condition } });
|
||||||
|
const pattern = new design();
|
||||||
|
expect(pattern.hooks.preRender.length).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Design constructor should load multiple conditional plugins (legacy)", () => {
|
||||||
const plugin = {
|
const plugin = {
|
||||||
name: "example",
|
name: "example",
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -114,6 +187,26 @@ it("Design constructor should load multiple conditional plugins", () => {
|
||||||
expect(pattern.hooks.preRender.length).to.equal(1);
|
expect(pattern.hooks.preRender.length).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Design constructor should load multiple conditional plugins (2022)", () => {
|
||||||
|
const plugin = {
|
||||||
|
name: "example",
|
||||||
|
version: 1,
|
||||||
|
hooks: {
|
||||||
|
preRender: function(svg, attributes) {
|
||||||
|
svg.attributes.add("freesewing:plugin-example", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const condition1 = () => true
|
||||||
|
const condition2 = () => false
|
||||||
|
const design = new freesewing.Design({ conditionalPlugins: [
|
||||||
|
{ plugin, condition: condition1 },
|
||||||
|
{ plugin, condition: condition2 },
|
||||||
|
]});
|
||||||
|
const pattern = new design();
|
||||||
|
expect(pattern.hooks.preRender.length).to.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
it("Design constructor should construct basic part order", () => {
|
it("Design constructor should construct basic part order", () => {
|
||||||
let design = new freesewing.Design({
|
let design = new freesewing.Design({
|
||||||
dependencies: { step4: "step3" },
|
dependencies: { step4: "step3" },
|
||||||
|
@ -209,7 +302,7 @@ it("Design constructor should handle Simon", () => {
|
||||||
let pattern = new design();
|
let pattern = new design();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Design constructor should add default hide() method to options", () => {
|
it("Pattern constructor should add default hide() method to options", () => {
|
||||||
const design = new freesewing.Design({
|
const design = new freesewing.Design({
|
||||||
foo: "bar",
|
foo: "bar",
|
||||||
options: {
|
options: {
|
||||||
|
@ -233,3 +326,19 @@ it("Design constructor should add default hide() method to options", () => {
|
||||||
expect(pattern.config.options.degree.hide()).to.be.false
|
expect(pattern.config.options.degree.hide()).to.be.false
|
||||||
expect(pattern.config.options.withHide.hide(pattern.settings)).to.be.true
|
expect(pattern.config.options.withHide.hide(pattern.settings)).to.be.true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("Should warn when passing plugins both as parameter and in the config", () => {
|
||||||
|
const design = new freesewing.Design({plugins: [{}]}, {});
|
||||||
|
expect(design.config.warnings.length).to.equal(2)
|
||||||
|
expect(design.config.warnings[0]).to.equal('Passing plugins to the Design constructor both as a second parameter and in the config is unsupported')
|
||||||
|
expect(design.config.warnings[1]).to.equal('Ignoring plugins passed as parameter. Only config.plugins will be used.')
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Should warn when passing conditionalPlugins both as parameter and in the config", () => {
|
||||||
|
const design = new freesewing.Design({conditionalPlugins: [{}]}, false, {});
|
||||||
|
expect(design.config.warnings.length).to.equal(2)
|
||||||
|
expect(design.config.warnings[0]).to.equal('Passing conditionalPlugins to the Design constructor both as a third parameter and in the config is unsupported.')
|
||||||
|
expect(design.config.warnings[1]).to.equal('Ignoring conditionalPlugins passes as parameter. Only config.conditionalPlugins will be used.')
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ it("Pattern constructor should initialize object", () => {
|
||||||
expect(pattern.settings.options.percentage).to.equal(0.3);
|
expect(pattern.settings.options.percentage).to.equal(0.3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it("Should load percentage options", () => {
|
it("Should load percentage options", () => {
|
||||||
let pattern = new freesewing.Pattern({
|
let pattern = new freesewing.Pattern({
|
||||||
options: {
|
options: {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue