1
0
Fork 0

wip: Added part-level dependencies

Restructured code a bit to handle all part-level config in one call.

Removed check in shorthand for debug as it's no longer used.
Updated tests to not fall over on different error message format in
newer NodeJS versions
This commit is contained in:
joostdecock 2022-08-13 18:33:06 +02:00
parent 689f908f68
commit 4cf9c3bd47
5 changed files with 187 additions and 175 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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);
});