From 8ccab0df2156508272a42e3abe40b2b7133395df Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Tue, 18 Apr 2023 18:47:49 -0400 Subject: [PATCH] chore (core) cleanup --- packages/core/src/pattern/index.mjs | 2 +- packages/core/src/pattern/pattern-drafter.mjs | 28 ++---- .../core/src/pattern/pattern-renderer.mjs | 92 +++++++++---------- packages/core/src/utils.mjs | 6 +- packages/core/tests/pattern-other.test.mjs | 3 +- packages/core/tests/pattern-renderer.test.mjs | 39 ++++++++ packages/core/tests/stacks.test.mjs | 2 +- packages/core/tests/utils.test.mjs | 2 +- 8 files changed, 97 insertions(+), 77 deletions(-) create mode 100644 packages/core/tests/pattern-renderer.test.mjs diff --git a/packages/core/src/pattern/index.mjs b/packages/core/src/pattern/index.mjs index b4079e74dd3..41e95cf02d8 100644 --- a/packages/core/src/pattern/index.mjs +++ b/packages/core/src/pattern/index.mjs @@ -9,7 +9,7 @@ import { __loadPatternDefaults } from '../config.mjs' import { PatternConfig } from './pattern-config.mjs' import { PatternDrafter } from './pattern-drafter.mjs' import { PatternSampler } from './pattern-sampler.mjs' -import { PatternPlugins, getPluginName } from './pattern-plugins.mjs' +import { PatternPlugins } from './pattern-plugins.mjs' import { PatternRenderer } from './pattern-renderer.mjs' import cloneDeep from 'lodash.clonedeep' diff --git a/packages/core/src/pattern/pattern-drafter.mjs b/packages/core/src/pattern/pattern-drafter.mjs index 806abebbd3e..da6f6ef87bb 100644 --- a/packages/core/src/pattern/pattern-drafter.mjs +++ b/packages/core/src/pattern/pattern-drafter.mjs @@ -6,25 +6,6 @@ export function PatternDrafter(pattern) { this.pattern = pattern } -Object.defineProperty(PatternDrafter.prototype, 'activeSet', { - get: function () { - return this.pattern.activeSet - }, - set: function (newVal) { - this.pattern.activeSet = newVal - }, -}) - -Object.defineProperty(PatternDrafter.prototype, 'activePart', { - get: function () { - return this.pattern.activePart - }, - set: function (newVal) { - this.pattern.activePart = newVal - this.activeStore.set('activePart', newVal) - }, -}) - /** * Drafts this pattern, aka the raison d'etre of FreeSewing * @@ -66,6 +47,10 @@ PatternDrafter.prototype.draft = function () { } PatternDrafter.prototype.draftPartForSet = function (partName, set) { + // gotta protect against attacks + if (set === '__proto__') { + throw new Error('malicious attempt at altering Object.prototype. Stopping action') + } this.__useSet(set) this.__createPartForSet(partName, set) @@ -77,7 +62,8 @@ PatternDrafter.prototype.draftPartForSet = function (partName, set) { return } - this.activePart = partName + this.pattern.activePart = partName + this.activeStore.set('activePart', partName) try { this.pattern.__runHooks('prePartDraft') const result = configPart.draft(this.pattern.parts[set][partName].shorthand()) @@ -222,7 +208,7 @@ PatternDrafter.prototype.__snappedPercentageOption = function (optionName, set) } PatternDrafter.prototype.__useSet = function (set = 0) { - this.activeSet = set + this.pattern.activeSet = set this.activeSettings = this.pattern.settings[set] this.activeStore = this.pattern.setStores[set] } diff --git a/packages/core/src/pattern/pattern-renderer.mjs b/packages/core/src/pattern/pattern-renderer.mjs index 6215e0c5543..071553a9341 100644 --- a/packages/core/src/pattern/pattern-renderer.mjs +++ b/packages/core/src/pattern/pattern-renderer.mjs @@ -36,38 +36,36 @@ PatternRenderer.prototype.getRenderProps = function () { autoLayout: this.pattern.autoLayout, settings: this.pattern.settings, parts: [], + stacks: {}, } - for (const set of this.pattern.parts) { - const setParts = {} - for (let p in set) { - if (!set[p].hidden) { - setParts[p] = { - ...set[p].asProps(), - store: this.pattern.setStores[set[p].set], + for (const partSet of this.pattern.parts) { + const setPartProps = {} + for (let partName in partSet) { + const part = partSet[partName] + if (!part.hidden) { + setPartProps[partName] = { + ...partSet[partName].asProps(), + store: this.pattern.setStores[part.set], } - } else if (this.pattern.setStores[set.set]) { - this.pattern.setStores[set.set].log.info( - `Part${p} is hidden in set ${set.set}. Not adding to render props` + } else if (this.pattern.setStores[part.set]) { + this.pattern.setStores[part.set].log.info( + `Part ${partName} is hidden in set ${part.set}. Not adding to render props` ) } } - props.parts.push(setParts) + props.parts.push(setPartProps) } - props.stacks = {} + for (let s in this.pattern.stacks) { if (!this.pattern.__isStackHidden(s)) { props.stacks[s] = this.pattern.stacks[s].asProps() } else this.pattern.store.log.info(`Stack ${s} is hidden. Skipping in render props.`) } + props.logs = { pattern: this.pattern.store.logs, - sets: this.pattern.setStores.map((store) => ({ - debug: store.logs.debug, - info: store.logs.info, - error: store.logs.error, - warning: store.logs.warning, - })), + sets: this.pattern.setStores.map((store) => store.logs), } this.svg.__runHooks('postRender') @@ -107,7 +105,7 @@ PatternRenderer.prototype.__stack = function () { */ PatternRenderer.prototype.__pack = function () { this.pattern.__runHooks('preLayout') - const { settings, setStores, parts } = this.pattern + const { settings, setStores, activeSet } = this.pattern for (const set in settings) { if (setStores[set].logs.error.length > 0) { setStores[set].log.warning(`One or more errors occured. Not packing pattern parts`) @@ -123,39 +121,37 @@ PatternRenderer.prototype.__pack = function () { stack.attributes.remove('transform') if (!this.pattern.__isStackHidden(key)) { stack.home() - if (settings[0].layout === true) + if (settings[activeSet].layout === true) bins.push({ id: key, width: stack.width, height: stack.height }) - else { - if (this.width < stack.width) this.width = stack.width - if (this.height < stack.height) this.height = stack.height + } + } + if (settings[activeSet].layout === true) { + // some plugins will add a width constraint to the settings, but we can safely pass undefined if not + let size = pack(bins, { inPlace: true, maxWidth: settings[0].maxWidth }) + this.autoLayout.width = size.width + this.autoLayout.height = size.height + + for (let bin of bins) { + let stack = this.stacks[bin.id] + this.autoLayout.stacks[bin.id] = { + move: { + x: bin.x + stack.layout.move.x, + y: bin.y + stack.layout.move.y, + }, } } } - if (settings[0].layout === true) { - // some plugins will add a width constraint to the settings, but we can safely pass undefined if not - let size = pack(bins, { inPlace: true, maxWidth: settings[0].maxWidth }) - for (let bin of bins) { - this.autoLayout.stacks[bin.id] = { move: {} } - let stack = this.stacks[bin.id] - if (bin.x !== 0 || bin.y !== 0) { - stack.attr('transform', `translate(${bin.x}, ${bin.y})`) - } - this.autoLayout.stacks[bin.id].move = { - x: bin.x + stack.layout.move.x, - y: bin.y + stack.layout.move.y, - } - } - this.width = size.width - this.height = size.height - } else if (typeof settings[0].layout === 'object') { - this.width = settings[0].layout.width - this.height = settings[0].layout.height - for (let stackId of Object.keys(settings[0].layout.stacks)) { - // Some parts are added by late-stage plugins - if (this.stacks[stackId]) { - let transforms = settings[this.activeStack || 0].layout.stacks[stackId] - this.stacks[stackId].generateTransform(transforms) - } + + const packedLayout = + typeof settings[activeSet].layout === 'object' ? settings[activeSet].layout : this.autoLayout + + this.width = packedLayout.width + this.height = packedLayout.height + for (let stackId of Object.keys(packedLayout.stacks)) { + // Some parts are added by late-stage plugins + if (this.stacks[stackId]) { + let transforms = packedLayout.stacks[stackId] + this.stacks[stackId].generateTransform(transforms) } } diff --git a/packages/core/src/utils.mjs b/packages/core/src/utils.mjs index 30840c42b4e..aea6af3d686 100644 --- a/packages/core/src/utils.mjs +++ b/packages/core/src/utils.mjs @@ -320,7 +320,7 @@ export const generateStackTransform = ( // add the scaling to the transforms if (scaleX + scaleY < 2) { - transforms.push(`scale(${scaleX} ${scaleY})`) + transforms.push(`scale(${scaleX}, ${scaleY})`) } if (rotate) { @@ -331,11 +331,11 @@ export const generateStackTransform = ( } // add the rotation around the center to the transforms - transforms.push(`rotate(${rotate} ${center.x} ${center.y})`) + transforms.push(`rotate(${rotate}, ${center.x}, ${center.y})`) } // put the translation before any other transforms to avoid having to make complex calculations once the matrix has been rotated or scaled - if (xTotal !== 0 || yTotal !== 0) transforms.unshift(`translate(${xTotal} ${yTotal})`) + if (xTotal !== 0 || yTotal !== 0) transforms.unshift(`translate(${xTotal}, ${yTotal})`) return { transform: transforms.join(' '), diff --git a/packages/core/tests/pattern-other.test.mjs b/packages/core/tests/pattern-other.test.mjs index 7b7a51dde9c..46fd350cdda 100644 --- a/packages/core/tests/pattern-other.test.mjs +++ b/packages/core/tests/pattern-other.test.mjs @@ -159,7 +159,6 @@ describe('Pattern', () => { expect(pattern.setStores[0].logs.error[0][0]).to.equal('Unable to draft part `test` (set 0)') }) - // FIXME: Add assertions here it('Handle layout object', () => { const part = { name: 'test', @@ -174,7 +173,7 @@ describe('Pattern', () => { layout: { stacks: { test: { flipX: true } }, width: 300, height: 400 }, }) const props = pattern.draft().getRenderProps() - expect(props.stacks.test.attributes.get('transform')).to.equal('scale(-1 1)') + expect(props.stacks.test.attributes.get('transform')).to.equal('scale(-1, 1)') expect(props.width).to.equal(300) expect(props.height).to.equal(400) }) diff --git a/packages/core/tests/pattern-renderer.test.mjs b/packages/core/tests/pattern-renderer.test.mjs new file mode 100644 index 00000000000..1888363d577 --- /dev/null +++ b/packages/core/tests/pattern-renderer.test.mjs @@ -0,0 +1,39 @@ +import chai from 'chai' +import { Design } from '../src/index.mjs' + +const expect = chai.expect + +describe('Pattern Rendering', () => { + describe('Pattern.prototype.getRenderProps()', () => { + describe('Hidden parts and stacks', () => { + const part = { + name: 'test', + draft: ({ part }) => { + part.hide() + return part + }, + } + + const design = new Design({ parts: [part] }) + const pattern = new design({}) + const props = pattern.draft().getRenderProps() + + it('Should not include hidden parts', () => { + expect(props.parts[0]).not.to.have.property('test') + }) + it('Should log that it has skipped a hidden part', () => { + expect(props.logs.sets[0].info).to.include( + 'Part test is hidden in set 0. Not adding to render props' + ) + }) + it('Should not include hidden stacks', () => { + expect(props.stacks).not.to.have.property('test') + }) + it('Should log that it has skipped a hidden stack', () => { + expect(props.logs.pattern.info).to.include( + 'Stack test is hidden. Skipping in render props.' + ) + }) + }) + }) +}) diff --git a/packages/core/tests/stacks.test.mjs b/packages/core/tests/stacks.test.mjs index 416dfc45743..f1b67b25ad1 100644 --- a/packages/core/tests/stacks.test.mjs +++ b/packages/core/tests/stacks.test.mjs @@ -170,7 +170,7 @@ describe('Stacks', () => { }, }) expect(pattern.stacks.test.attributes.list.transform.length).to.equal(1) - expect(pattern.stacks.test.attributes.list.transform[0]).to.equal('translate(10 20)') + expect(pattern.stacks.test.attributes.list.transform[0]).to.equal('translate(10, 20)') }) }) diff --git a/packages/core/tests/utils.test.mjs b/packages/core/tests/utils.test.mjs index 40ac8bf6467..bd0817f0ee6 100644 --- a/packages/core/tests/utils.test.mjs +++ b/packages/core/tests/utils.test.mjs @@ -492,6 +492,6 @@ describe('Utils', () => { const pattern = new design() const props = pattern.draft().getRenderProps() const transform = generateStackTransform(30, 60, 90, true, true, props.stacks.test) - expect(transform.transform).to.equal('translate(51 138) scale(-1 -1) rotate(90 10.5 39)') + expect(transform.transform).to.equal('translate(51, 138) scale(-1, -1) rotate(90, 10.5, 39)') }) })