1
0
Fork 0

wip(core): Work on v3

This commit is contained in:
Joost De Cock 2022-08-27 09:28:36 +02:00
parent cd76e75101
commit e4b5d52aaf
3 changed files with 77 additions and 135 deletions

View file

@ -1,43 +1,40 @@
import Pattern from './pattern.mjs'
import { addPartConfig } from './utils.mjs'
/*
* The Design constructor. Returns a Pattern constructor
* So it's sort of a super-constructor
*/
export default function Design(config, plugins = false, conditionalPlugins = false) {
// Add part options/measurements/optionalMeasurements to config
if (!config.options) config.options = {}
if (!config.measurements) config.measurements = []
if (!config.optionalMeasurements) config.optionalMeasurements = []
if (Array.isArray(config.parts)) {
export default function Design(config) {
// Merge config with defaults
config = {
parts: [],
options: {},
measurements: [],
optionalMeasurements: [],
plugins: [],
conditionalPlugins: [],
...config
}
const parts = {}
for (const part of config.parts) {
if (typeof part === 'object') {
parts[part.name] = part
config = addPartConfig(parts[part.name], config)
} else if (typeof part === 'string') {
parts[part] = part
} else throw("Part should be passed as a name of part config object")
}
else throw("Invalid part configuration. Part is not an object")
}
// Replace config.parts with the resolved config
config.parts = parts
}
// Ensure all options have a hide() method
config.options = optionsWithHide(config.options)
// Ensure all options have a hide() method and menu property
config.options = completeOptions(config.options)
// A place to store deprecation and other warnings before we even have a pattern instantiated
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) {
Pattern.call(this, config)
@ -66,55 +63,22 @@ export default function Design(config, plugins = false, conditionalPlugins = fal
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
* As this always return false, the option will never be hidden
*/
const hide = () => false // The default hide() method
const hide = () => false
/*
* Helper method to add the default hide() method to options who lack one
* as well as set the `menu` property to false (if it's missing)
*/
const optionsWithHide = options => {
const completeOptions = options => {
if (options) {
for (const option in options) {
if (typeof options[option] === 'object') options[option] = { hide, ...options[option] }
if (typeof options[option] === 'object') {
options[option] = { hide, menu: false, ...options[option] }
}
}
}

View file

@ -57,8 +57,8 @@ Part.prototype.getId = function (prefix = '') {
/** Returns a value formatted for units provided in settings */
Part.prototype.unitsClosure = function (value) {
let self = this
let method = function (value) {
const self = this
const method = function (value) {
if (self.context.settings.debug && typeof value !== 'number')
self.context.raise.debug(
`Calling \`units(value)\` but \`value\` is not a number (\`${typeof value}\`)`

View file

@ -15,7 +15,7 @@ import Svg from './svg.mjs'
import Store from './store.mjs'
import Hooks from './hooks.mjs'
import Attributes from './attributes.mjs'
import pkg from '../package.json'
import { version } from '../package.json' assert { type: 'json' }
export default function Pattern(config = { options: {} }) {
@ -28,41 +28,45 @@ export default function Pattern(config = { options: {} }) {
margin: 2,
scale: 1,
layout: true,
debug: true,
debug: false,
options: {},
absoluteOptions: {},
}
// Object to hold events
this.events = {
info: [],
warning: [],
error: [],
debug: [],
error: [],
info: [],
suggestion: [],
warning: [],
}
// Raise methods - Make events and settings avialable in them
const events = this.events
const settings = this.settings
this.raise = {
debug: function (data) {
// Debug only if debug is active
if (settings.debug) events.debug.push(data)
},
error: function (data) {
events.error.push(data)
},
info: function (data) {
events.info.push(data)
},
suggestion: function (data) {
events.info.push(data)
},
warning: function (data) {
events.warning.push(data)
},
error: function (data) {
events.error.push(data)
},
debug: function (data) {
// Debug only if debug is active
if (settings.debug) events.debug.push(data)
},
}
// Say hi
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:${version}\``
)
// More things that go in a pattern
@ -72,7 +76,6 @@ export default function Pattern(config = { options: {} }) {
this.is = '' // Will be set when drafting/sampling
this.autoLayout = { parts: {} } // Will hold auto-generated layout
this.cutList = {} // Will hold the cutlist
this.store = new Store(this.raise) // Store for sharing data across parts
this.parts = {} // Parts container
this.hooks = new Hooks() // Hooks container
@ -81,6 +84,7 @@ export default function Pattern(config = { options: {} }) {
this.Snippet = Snippet // Snippet constructor
this.Attributes = Attributes // Attributes constructor
this.initialized = 0 // Keep track of init calls
this.macros = {} // Macros
if (typeof this.config.dependencies === 'undefined') this.config.dependencies = {}
if (typeof this.config.inject === 'undefined') this.config.inject = {}
@ -94,9 +98,6 @@ export default function Pattern(config = { options: {} }) {
}
}
// Macros
this.macros = {}
// Context object to add to Part closure
const context = {
parts: this.parts,
@ -148,11 +149,18 @@ Pattern.prototype.addOptions = function(options={}) {
return this
}
/* Utility method to get the (initialized) config */
Pattern.prototype.getConfig = function () {
this.init()
return this.config
}
/* Utility method to get the (initialized) part list */
Pattern.prototype.getPartList = function () {
this.init()
return Object.keys(this.config.parts) || []
}
/*
* Defer some things that used to happen in the constructor to
@ -163,11 +171,8 @@ Pattern.prototype.init = function () {
// Resolve all dependencies
this.dependencies = this.config.dependencies
this.inject = this.config.inject
this.hide = this.config.hide
if (typeof this.config.parts === 'object') {
this.__parts = this.config.parts
this.preresolveDependencies()
}
this.resolvedDependencies = this.resolveDependencies(this.dependencies)
this.config.resolvedDependencies = this.resolvedDependencies
this.config.draftOrder = this.draftOrder(this.resolvedDependencies)
@ -250,8 +255,7 @@ Pattern.prototype.runHooks = function (hookName, data = false) {
/*
* Allows adding a part at run-time
*/
Pattern.prototype.addPart = function (part, name=false) {
if (!part.draft) part = decoratePartDependency(part, name)
Pattern.prototype.addPart = function (part) {
if (typeof part?.draft === 'function') {
if (part.name) {
this.config.parts[part.name] = part
@ -260,7 +264,7 @@ Pattern.prototype.addPart = function (part, name=false) {
}
else this.raise.error(`Part must have a name`)
}
else this.raise.warning(`Cannot attach part ${name} because it is not a part`)
else this.raise.error(`Part must have a draft() method`)
return this
}
@ -309,7 +313,6 @@ Pattern.prototype.draft = function () {
}
if (this.needs(partName)) {
// Draft part
const method = 'draft' + capitalize(partName)
if (typeof this.__parts?.[partName]?.draft === 'function') {
// 2022 way - Part is contained in config
try {
@ -319,25 +322,7 @@ Pattern.prototype.draft = function () {
this.raise.error([`Unable to draft part \`${partName}\``, err])
}
}
else if (typeof this[method] === 'function') {
// Legacy way - Part is attached to the prototype
this.raise.warning(`Attaching part methods to the Pattern prototype is deprecated and will be removed in FreeSewing v3 (part: \`${partName}\`)`)
try {
this.parts[partName] = this[method](this.parts[partName])
if (this.parts[partName].render ) this.cutList[partName] = this.parts[partName].cut
} catch (err) {
this.raise.error([`Unable to draft part \`${partName}\``, err])
}
}
else {
this.raise.error(`Unable to draft pattern. Part is not available in iether legacy or 2022`)
throw new Error('Method "' + method + '" on pattern object is not callable')
}
if (typeof this.parts[partName] === 'undefined') {
this.raise.error(
`Result of \`pattern.${method}\` was \`undefined\`. Did you forget to return the \`Part\` object?`
)
}
else this.raise.error(`Unable to draft pattern. Part.draft() is not callable`)
try {
this.parts[partName].render =
this.parts[partName].render === false ? false : this.wants(partName)
@ -689,7 +674,7 @@ Pattern.prototype.addDependency = function (name, part, dep) {
return this
}
/** Filter optional measurements out of they are also required measurements */
/** Filter optional measurements out if they are also required measurements */
Pattern.prototype.filterOptionalMeasurements = function () {
this.config.optionalMeasurements = this.config.optionalMeasurements.filter(
m => this.config.measurements.indexOf(m) === -1
@ -755,11 +740,11 @@ Pattern.prototype.resolveDependencies = function (graph = this.dependencies) {
}
// Include parts outside the dependency graph
if (typeof this.config.parts === 'object') {
for (const part of Object.values(this.config.parts)) {
if (typeof part === 'string' && typeof this.dependencies[part] === 'undefined') this.dependencies[part] = []
}
}
//if (typeof this.config.parts === 'object') {
// for (const part of Object.values(this.config.parts)) {
// if (typeof part === 'string' && typeof this.dependencies[part] === 'undefined') this.dependencies[part] = []
// }
//}
let resolved = {}
let seen = {}
@ -796,26 +781,19 @@ Pattern.prototype.needs = function (partName) {
/* Checks whether a part is hidden in the config */
Pattern.prototype.isHidden = function (partName) {
if (Array.isArray(this.hide)) {
if (this.hide.indexOf(partName) !== -1) return true
}
// 2022 style
if (this.__parts?.[partName]?.hide) return true
return false
return (this.__parts?.[partName]?.hide) ? true : false
}
/** Determines whether a part is wanted by the user
* This depends on the 'only' setting
*/
Pattern.prototype.wants = function (partName) {
if (typeof this.settings.only === 'undefined' || this.settings.only === false) {
if (!this.settings?.only === 'undefined' || this.settings.only === false) {
if (this.isHidden(partName)) return false
} else if (typeof this.settings.only === 'string') {
if (this.settings.only === partName) return true
return false
} else if (Array.isArray(this.settings.only)) {
for (let part of this.settings.only) {
}
else if (typeof this.settings.only === 'string') return (this.settings.only === partName)
else if (Array.isArray(this.settings.only)) {
for (const part of this.settings.only) {
if (part === partName) return true
}
return false
@ -851,8 +829,8 @@ Pattern.prototype.getRenderProps = function () {
props.events = {
debug: this.events.debug,
info: this.events.info,
warning: this.events.warning,
error: this.events.error,
warning: this.events.warning,
}
props.cutList = this.cutList
props.parts = {}