diff --git a/packages/core/src/design.js b/packages/core/src/design.js index 2ab824167c5..1e5cf181f1b 100644 --- a/packages/core/src/design.js +++ b/packages/core/src/design.js @@ -1,5 +1,5 @@ import Pattern from './pattern' -import { addOptions, addMeasurements, addOptionalMeasurements } from './utils.js' +import { addPartConfig } from './utils.js' /* * The Design constructor. Returns a Pattern constructor @@ -11,11 +11,7 @@ export default function Design(config, plugins = false, conditionalPlugins = fal if (!config.measurements) config.measurements = [] if (!config.optionalMeasurements) config.optionalMeasurements = [] if (config.parts) { - for (const partName in config.parts) { - config = addOptions(config.parts[partName], config) - config = addMeasurements(config.parts[partName], config) - config = addOptionalMeasurements(config.parts[partName], config) - } + for (const partName in config.parts) config = addPartConfig(config.parts[partName], config) } // Ensure all options have a hide() method diff --git a/packages/core/src/part.js b/packages/core/src/part.js index 8b3dc8e2dac..06130c64ed1 100644 --- a/packages/core/src/part.js +++ b/packages/core/src/part.js @@ -180,9 +180,9 @@ Part.prototype.units = function (input) { /** Returns an object with shorthand access for pattern design */ Part.prototype.shorthand = function () { - let complete = this.context.settings.complete ? true : false - let paperless = this.context.settings.paperless === true ? true : false - let sa = this.context.settings.complete ? this.context.settings.sa || 0 : 0 + const complete = this.context.settings.complete ? true : false + const paperless = this.context.settings.paperless === true ? true : false + const sa = this.context.settings.complete ? this.context.settings.sa || 0 : 0 const shorthand = { sa, scale: this.context.settings.scale, @@ -198,154 +198,142 @@ Part.prototype.shorthand = function () { removeCut: this.removeCut, } - if (this.context.settings.debug) { - // We'll need this - let self = this + // We'll need this + let self = this - // Wrap the Point constructor so objects can raise events - shorthand.Point = function (x, y) { - Point.apply(this, [x, y, true]) - Object.defineProperty(this, 'raise', { value: self.context.raise }) - } - shorthand.Point.prototype = Object.create(Point.prototype) - // Wrap the Path constructor so objects can raise events - shorthand.Path = function () { - Path.apply(this, [true]) - Object.defineProperty(this, 'raise', { value: self.context.raise }) - } - shorthand.Path.prototype = Object.create(Path.prototype) - // Wrap the Snippet constructor so objects can raise events - shorthand.Snippet = function (def, anchor) { - Snippet.apply(this, [def, anchor, true]) - Snippet.apply(this, arguments) - Object.defineProperty(this, 'raise', { value: self.context.raise }) - } - shorthand.Snippet.prototype = Object.create(Snippet.prototype) - - // Proxy the points object - const pointsProxy = { - get: function () { - return Reflect.get(...arguments) - }, - set: (points, name, value) => { - // Constructor checks - if (value instanceof Point !== true) - self.context.raise.warning( - `\`points.${name}\` was set with a value that is not a \`Point\` object` - ) - if (value.x == null || !utils.isCoord(value.x)) - self.context.raise.warning( - `\`points.${name}\` was set with a \`x\` parameter that is not a \`number\`` - ) - if (value.y == null || !utils.isCoord(value.y)) - self.context.raise.warning( - `\`points.${name}\` was set with a \`y\` parameter that is not a \`number\`` - ) - try { - value.name = name - } catch (err) { - self.context.raise.warning(`Could not set \`name\` property on \`points.${name}\``) - } - return (self.points[name] = value) - }, - } - shorthand.points = new Proxy(this.points || {}, pointsProxy) - // Proxy the paths object - const pathsProxy = { - get: function () { - return Reflect.get(...arguments) - }, - set: (paths, name, value) => { - // Constructor checks - if (value instanceof Path !== true) - self.context.raise.warning( - `\`paths.${name}\` was set with a value that is not a \`Path\` object` - ) - try { - value.name = name - } catch (err) { - self.context.raise.warning(`Could not set \`name\` property on \`paths.${name}\``) - } - return (self.paths[name] = value) - }, - } - shorthand.paths = new Proxy(this.paths || {}, pathsProxy) - // Proxy the snippets object - const snippetsProxy = { - get: function (target, prop, receiver) { - return Reflect.get(...arguments) - }, - set: (snippets, name, value) => { - // Constructor checks - if (value instanceof Snippet !== true) - self.context.raise.warning( - `\`snippets.${name}\` was set with a value that is not a \`Snippet\` object` - ) - if (typeof value.def !== 'string') - self.context.raise.warning( - `\`snippets.${name}\` was set with a \`def\` parameter that is not a \`string\`` - ) - if (value.anchor instanceof Point !== true) - self.context.raise.warning( - `\`snippets.${name}\` was set with an \`anchor\` parameter that is not a \`Point\`` - ) - try { - value.name = name - } catch (err) { - self.context.raise.warning(`Could not set \`name\` property on \`snippets.${name}\``) - } - return (self.snippets[name] = value) - }, - } - shorthand.snippets = new Proxy(this.snippets || {}, snippetsProxy) - // Proxy the measurements object - const measurementsProxy = { - get: function (measurements, name) { - if (typeof measurements[name] === 'undefined') - self.context.raise.warning( - `Tried to access \`measurements.${name}\` but it is \`undefined\`` - ) - return Reflect.get(...arguments) - }, - set: (measurements, name, value) => (self.context.settings.measurements[name] = value), - } - shorthand.measurements = new Proxy(this.context.settings.measurements || {}, measurementsProxy) - // Proxy the options object - const optionsProxy = { - get: function (options, name) { - if (typeof options[name] === 'undefined') - self.context.raise.warning(`Tried to access \`options.${name}\` but it is \`undefined\``) - return Reflect.get(...arguments) - }, - set: (options, name, value) => (self.context.settings.options[name] = value), - } - shorthand.options = new Proxy(this.context.settings.options || {}, optionsProxy) - // Proxy the absoluteOptions object - const absoluteOptionsProxy = { - get: function (absoluteOptions, name) { - if (typeof absoluteOptions[name] === 'undefined') - self.context.raise.warning( - `Tried to access \`absoluteOptions.${name}\` but it is \`undefined\`` - ) - return Reflect.get(...arguments) - }, - set: (absoluteOptions, name, value) => (self.context.settings.absoluteOptions[name] = value), - } - shorthand.absoluteOptions = new Proxy( - this.context.settings.absoluteOptions || {}, - absoluteOptionsProxy - ) - } else { - shorthand.Point = Point - shorthand.Path = Path - shorthand.Snippet = Snippet - shorthand.points = this.points || {} - shorthand.paths = this.paths || {} - shorthand.snippets = this.snippets || {} - shorthand.measurements = this.context.settings.measurements || {} - shorthand.options = this.context.settings.options || {} - shorthand.absoluteOptions = this.context.settings.absoluteOptions || {} + // Wrap the Point constructor so objects can raise events + shorthand.Point = function (x, y) { + Point.apply(this, [x, y, true]) + Object.defineProperty(this, 'raise', { value: self.context.raise }) } + shorthand.Point.prototype = Object.create(Point.prototype) + // Wrap the Path constructor so objects can raise events + shorthand.Path = function () { + Path.apply(this, [true]) + Object.defineProperty(this, 'raise', { value: self.context.raise }) + } + shorthand.Path.prototype = Object.create(Path.prototype) + // Wrap the Snippet constructor so objects can raise events + shorthand.Snippet = function (def, anchor) { + Snippet.apply(this, [def, anchor, true]) + Snippet.apply(this, arguments) + Object.defineProperty(this, 'raise', { value: self.context.raise }) + } + shorthand.Snippet.prototype = Object.create(Snippet.prototype) + + // Proxy the points object + const pointsProxy = { + get: function () { + return Reflect.get(...arguments) + }, + set: (points, name, value) => { + // Constructor checks + if (value instanceof Point !== true) + self.context.raise.warning( + `\`points.${name}\` was set with a value that is not a \`Point\` object` + ) + if (value.x == null || !utils.isCoord(value.x)) + self.context.raise.warning( + `\`points.${name}\` was set with a \`x\` parameter that is not a \`number\`` + ) + if (value.y == null || !utils.isCoord(value.y)) + self.context.raise.warning( + `\`points.${name}\` was set with a \`y\` parameter that is not a \`number\`` + ) + try { + value.name = name + } catch (err) { + self.context.raise.warning(`Could not set \`name\` property on \`points.${name}\``) + } + return (self.points[name] = value) + }, + } + shorthand.points = new Proxy(this.points || {}, pointsProxy) + // Proxy the paths object + const pathsProxy = { + get: function () { + return Reflect.get(...arguments) + }, + set: (paths, name, value) => { + // Constructor checks + if (value instanceof Path !== true) + self.context.raise.warning( + `\`paths.${name}\` was set with a value that is not a \`Path\` object` + ) + try { + value.name = name + } catch (err) { + self.context.raise.warning(`Could not set \`name\` property on \`paths.${name}\``) + } + return (self.paths[name] = value) + }, + } + shorthand.paths = new Proxy(this.paths || {}, pathsProxy) + // Proxy the snippets object + const snippetsProxy = { + get: function (target, prop, receiver) { + return Reflect.get(...arguments) + }, + set: (snippets, name, value) => { + // Constructor checks + if (value instanceof Snippet !== true) + self.context.raise.warning( + `\`snippets.${name}\` was set with a value that is not a \`Snippet\` object` + ) + if (typeof value.def !== 'string') + self.context.raise.warning( + `\`snippets.${name}\` was set with a \`def\` parameter that is not a \`string\`` + ) + if (value.anchor instanceof Point !== true) + self.context.raise.warning( + `\`snippets.${name}\` was set with an \`anchor\` parameter that is not a \`Point\`` + ) + try { + value.name = name + } catch (err) { + self.context.raise.warning(`Could not set \`name\` property on \`snippets.${name}\``) + } + return (self.snippets[name] = value) + }, + } + shorthand.snippets = new Proxy(this.snippets || {}, snippetsProxy) + // Proxy the measurements object + const measurementsProxy = { + get: function (measurements, name) { + if (typeof measurements[name] === 'undefined') + self.context.raise.warning( + `Tried to access \`measurements.${name}\` but it is \`undefined\`` + ) + return Reflect.get(...arguments) + }, + set: (measurements, name, value) => (self.context.settings.measurements[name] = value), + } + shorthand.measurements = new Proxy(this.context.settings.measurements || {}, measurementsProxy) + // Proxy the options object + const optionsProxy = { + get: function (options, name) { + if (typeof options[name] === 'undefined') + self.context.raise.warning(`Tried to access \`options.${name}\` but it is \`undefined\``) + return Reflect.get(...arguments) + }, + set: (options, name, value) => (self.context.settings.options[name] = value), + } + shorthand.options = new Proxy(this.context.settings.options || {}, optionsProxy) + // Proxy the absoluteOptions object + const absoluteOptionsProxy = { + get: function (absoluteOptions, name) { + if (typeof absoluteOptions[name] === 'undefined') + self.context.raise.warning( + `Tried to access \`absoluteOptions.${name}\` but it is \`undefined\`` + ) + return Reflect.get(...arguments) + }, + set: (absoluteOptions, name, value) => (self.context.settings.absoluteOptions[name] = value), + } + shorthand.absoluteOptions = new Proxy( + this.context.settings.absoluteOptions || {}, + absoluteOptionsProxy + ) return shorthand } diff --git a/packages/core/src/pattern.js b/packages/core/src/pattern.js index 5859939c330..47115d07fc6 100644 --- a/packages/core/src/pattern.js +++ b/packages/core/src/pattern.js @@ -3,9 +3,7 @@ import { sampleStyle, capitalize, decoratePartDependency, - addOptions, - addMeasurements, - addOptionalMeasurements + addPartConfig, } from './utils.js' import Part from './part' import Point from './point' @@ -256,10 +254,8 @@ Pattern.prototype.addPart = function (part, name=false) { if (typeof part?.draft === 'function') { if (part.name) { this.config.parts[part.name] = part - // Add part options/measurements/optionalMeasurements to config - this.config = addOptions(part, this.config) - this.config = addMeasurements(part, this.config) - this.config = addOptionalMeasurements(part, this.config) + // Add part-level config to config + this.config = addPartConfig(part, this.config) } else this.raise.error(`Part must have a name`) } diff --git a/packages/core/src/utils.js b/packages/core/src/utils.js index a2d1e347978..f3d9a1a2f63 100644 --- a/packages/core/src/utils.js +++ b/packages/core/src/utils.js @@ -390,26 +390,26 @@ export const generatePartTransform = (x, y, rotate, flipX, flipY, part) => { export const decoratePartDependency = (obj, name) => (typeof obj === 'function') ? { draft: obj, name } : obj // Add part-level options -export const addOptions = (part, config) => { +const addPartOptions = (part, config) => { if (part.options) { for (const optionName in part.options) { config.options[optionName] = part.options[optionName] } } - if (part.from) addOptions(part.from, config) + if (part.from) addPartOptions(part.from, config) return config } // Add part-level measurements -export const addMeasurements = (part, config, list=false) => { +const addPartMeasurements = (part, config, list=false) => { if (!list) list = config.measurements ? [...config.measurements] : [] if (part.measurements) { for (const m of part.measurements) list.push(m) } - if (part.from) addMeasurements(part.from, config, list) + if (part.from) addPartMeasurements(part.from, config, list) // Weed out duplicates config.measurements = [...new Set(list)] @@ -418,7 +418,7 @@ export const addMeasurements = (part, config, list=false) => { } // Add part-level optional measurements -export const addOptionalMeasurements = (part, config, list=false) => { +const addPartOptionalMeasurements = (part, config, list=false) => { if (!list) list = config.optionalMeasurements ? [...config.optionalMeasurements] : [] @@ -428,7 +428,7 @@ export const addOptionalMeasurements = (part, config, list=false) => { if (config.measurements.indexOf(m) === -1) list.push(m) } } - if (part.from) addOptionalMeasurements(part.from, config, list) + if (part.from) addPartOptionalMeasurements(part.from, config, list) // Weed out duplicates config.optionalMeasurements = [...new Set(list)] @@ -437,3 +437,34 @@ export const addOptionalMeasurements = (part, config, list=false) => { } +const addDependencies = (dep, current) => { + // Current dependencies + const list = [] + if (Array.isArray(current)) list.push(...current) + else if (typeof current === 'string') list.push(current) + + if (Array.isArray(dep)) list.push(...dep) + else if (typeof dep === 'string') list.push(dep) + + return [...new Set(list)] +} + +// Add part-level dependencies +export const addPartDependencies = (part, config) => { + if (part.after) { + config.dependencies[part.name] = addDependencies(config.dependencies[part.name], part.after) + } + + return config +} + +export const addPartConfig = (part, config) => { + config = addPartOptions(part, config) + config = addPartMeasurements(part, config) + config = addPartOptionalMeasurements(part, config) + config = addPartDependencies(part, config) + + return config +} + + diff --git a/packages/core/tests/path.test.js b/packages/core/tests/path.test.js index feeff9c2ce0..1ba4510b19e 100644 --- a/packages/core/tests/path.test.js +++ b/packages/core/tests/path.test.js @@ -1017,7 +1017,7 @@ it("Should raise a warning when an insop operation used an falsy ID", () => { new freesewing.Path().withRaise(raise).noop('test').insop('test') } catch (err) { - expect(''+err).to.contain("Cannot read property 'ops") + expect(''+err).to.contain("Cannot read prop") } expect(invalid).to.equal(true); }); @@ -1057,10 +1057,11 @@ it("Should raise a warning when calling join without a path", () => { points.a = new Point(0,0) points.b = new Point(10,10) try { - paths.a = new Path().move(points.a).line(points.b).join() + //paths.a = new Path().move(points.a).line(points.b).join() + pattern.parts.a.paths.a = new Path().move(points.a).line(points.b).join() } catch (err) { - expect(''+err).to.contain("Cannot read property 'ops") + expect(''+err).to.contain("Cannot read prop") } expect(pattern.events.error.length).to.equal(1) expect(pattern.events.error[0]).to.equal("Called `Path.join(that)` but `that` is not a `Path` object") @@ -1074,7 +1075,7 @@ it("Should raise a warning when calling start on a path without drawing operatio new freesewing.Path().withRaise(raise).start() } catch (err) { - expect(''+err).to.contain("TypeError: Cannot read property") + expect(''+err).to.contain("TypeError: Cannot read prop") } expect(invalid).to.equal(true); }); @@ -1087,7 +1088,7 @@ it("Should raise a warning when calling end on a path without drawing operations new freesewing.Path().withRaise(raise).end() } catch (err) { - expect(''+err).to.contain("TypeError: Cannot read property") + expect(''+err).to.contain("TypeError: Cannot read prop") } expect(invalid).to.equal(true); }); @@ -1140,7 +1141,7 @@ it("Should raise a warning when splitting a path on a non-point", () => { path.split() } catch (err) { - expect(''+err).to.contain("TypeError: Cannot read property") + expect(''+err).to.contain("TypeError: Cannot read prop") } expect(invalid).to.equal(true); }); @@ -1164,7 +1165,7 @@ it("Should raise a warning when splitting a path on a non-point", () => { path.split() } catch (err) { - expect(''+err).to.contain("TypeError: Cannot read property") + expect(''+err).to.contain("TypeError: Cannot read prop") } expect(invalid).to.equal(true); });