diff --git a/packages/core/build.js b/packages/core/build.js index 7248b245c69..b18711312e6 100644 --- a/packages/core/build.js +++ b/packages/core/build.js @@ -48,8 +48,8 @@ let result ...options, minify: false, sourcemap: false, - outfile: 'tests/dist/index.mjs', - format: 'esm', + outfile: 'tests/dist/index.js', + format: 'cjs', external: [], }) .catch(() => process.exit(1)) diff --git a/packages/core/src/utils.js b/packages/core/src/utils.js index 35fcff2397d..5283ac147c8 100644 --- a/packages/core/src/utils.js +++ b/packages/core/src/utils.js @@ -397,6 +397,75 @@ const addPartOptions = (part, config) => { } } if (part.from) addPartOptions(part.from, config) + if (part.after) { + if (Array.isArray(part.after)) { + for (const dep of part.after) addPartOptions(dep, config) + } else addPartOptions(part.after, config) + } + + return config +} + +// Helper method for detecting a array with only strings +const isStringArray = val => (Array.isArray(val) && val.length > 0) + ? val.reduce((prev=true, cur) => (prev && typeof cur === 'string')) + : false +// Helper method for detecting an object +const isObject = obj => obj && typeof obj === 'object' + +// Hat-tip to jhildenbiddle => https://stackoverflow.com/a/48218209 +const mergeOptionSubgroup = (...objects) => objects.reduce((prev, obj) => { + Object.keys(obj).forEach(key => { + const pVal = prev[key]; + const oVal = obj[key]; + + if (Array.isArray(pVal) && Array.isArray(oVal)) { + prev[key] = pVal.concat(...oVal); + } + else if (isObject(pVal) && isObject(oVal)) { + prev[key] = mergeOptionSubgroup(pVal, oVal); + } + else { + prev[key] = oVal; + } + }) + + return prev +}, {}) + +const mergeOptionGroups = (cur, add) => { + if (isStringArray(cur) && isStringArray(add)) return [...new Set([...cur, ...add])] + else if (!Array.isArray(cur) && !Array.isArray(add)) return mergeOptionSubgroup(cur, add) + else { + const all = [...cur] + for (const entry of add) { + if (typeof add === 'string' && all.indexOf(entry) === -1) all.push(entry) + else all.push(entry) + } + return all + } + + return cur +} + +// Add part-level optionGroups +const addPartOptionGroups = (part, config) => { + if (typeof config.optionGroups === 'undefined') { + if (part.optionGroups) config.optionGroups = part.optionGroups + return config + } + if (part.optionGroups) { + for (const group in part.optionGroups) { + if (typeof config.optionGroups[group] === 'undefined') config.optionGroups[group] = part.optionGroups[group] + else config.optionGroups[group] = mergeOptionGroups(config.optionGroups[group], part.optionGroups[group]) + } + } + if (part.from) addPartOptionGroups(part.from, config) + if (part.after) { + if (Array.isArray(part.after)) { + for (const dep of part.after) addPartOptionGroups(dep, config) + } else addPartOptionGroups(part.after, config) + } return config } @@ -410,6 +479,11 @@ const addPartMeasurements = (part, config, list=false) => { for (const m of part.measurements) list.push(m) } if (part.from) addPartMeasurements(part.from, config, list) + if (part.after) { + if (Array.isArray(part.after)) { + for (const dep of part.after) addPartMeasurements(dep, config, list) + } else addPartMeasurements(part.after, config, list) + } // Weed out duplicates config.measurements = [...new Set(list)] @@ -429,6 +503,11 @@ const addPartOptionalMeasurements = (part, config, list=false) => { } } if (part.from) addPartOptionalMeasurements(part.from, config, list) + if (part.after) { + if (Array.isArray(part.after)) { + for (const dep of part.after) addPartOptionalMeasurements(dep, config, list) + } else addPartOptionalMeasurements(part.after, config, list) + } // Weed out duplicates config.optionalMeasurements = [...new Set(list)] @@ -471,6 +550,7 @@ export const addPartConfig = (part, config) => { config = addPartMeasurements(part, config) config = addPartOptionalMeasurements(part, config) config = addPartDependencies(part, config) + config = addPartOptionGroups(part, config) return config } diff --git a/packages/core/tests/pattern.test.js b/packages/core/tests/pattern.test.js index 57c8f4e383b..a3068ee8bb3 100644 --- a/packages/core/tests/pattern.test.js +++ b/packages/core/tests/pattern.test.js @@ -1,5 +1,5 @@ let expect = require("chai").expect; -let freesewing = require("../dist/index.js"); +let freesewing = require("./dist/index.js"); it("Pattern constructor should initialize object", () => { let pattern = new freesewing.Pattern({ foo: "bar", @@ -1023,3 +1023,97 @@ it("Design constructor should resolve nested dependencies (2022)", () => { expect(pattern.parts.partD.paths.d.ops[1].to.x).to.equal(44) expect(pattern.parts.partD.paths.d.ops[1].to.y).to.equal(44) }) +it("Pattern should merge optiongroups", () => { + const partA = { + name: "partA", + options: { optionA: { bool: true } }, + measurements: [ 'measieA' ], + optionalMeasurements: [ 'optmeasieA' ], + optionGroups: { + simple: ['simplea1', 'simplea2', 'simplea3'], + nested: { + nested1: [ 'nested1a1', 'nested1a2', 'nested1a3' ], + }, + subnested: { + subnested1: [ + 'subnested1a1', + 'subnested1a2', + 'subnested1a3', + { + subsubgroup: [ + 'subsuba1', + 'subsuba2', + { + subsubsubgroup: [ 'subsubsub1', 'simplea1' ], + } + ] + } + ] + } + }, + draft: part => part, + } + const partB = { + name: "partB", + from: partA, + options: { optionB: { pct: 12, min: 2, max: 20 } }, + measurements: [ 'measieB' ], + optionalMeasurements: [ 'optmeasieB', 'measieA' ], + optionGroups: { + simple: ['simpleb1', 'simpleb2', 'simpleb3'], + bsimple: ['bsimpleb1', 'bsimpleb2', 'bsimpleb3'], + nested: { + nested2: [ 'nested2b1', 'nested2b2', 'nested2b3' ], + }, + subnested: { + subnested1: [ + 'subnested1b1', + 'subnested1b2', + 'subnested1b3', + { + subsubgroup: [ + 'subsubb1', + 'subsubb2', + { + subsubsubgroup: [ 'bsubsubsub1', 'simplea1' ], + } + ] + } + ] + } + }, + draft: part => part, + } + let Design, pattern + try { + Design = new freesewing.Design({ parts: [ partB ] }); + pattern = new Design().init() + } catch(err) { + console.log(err) + } + const og = pattern.config.optionGroups + expect(og.simple.length).to.equal(6) + expect(og.simple.indexOf('simplea1') === -1).to.equal(false) + expect(og.simple.indexOf('simplea2') === -1).to.equal(false) + expect(og.simple.indexOf('simplea3') === -1).to.equal(false) + expect(og.simple.indexOf('simpleb1') === -1).to.equal(false) + expect(og.simple.indexOf('simpleb2') === -1).to.equal(false) + expect(og.simple.indexOf('simpleb3') === -1).to.equal(false) + expect(og.nested.nested1.length).to.equal(3) + expect(og.nested.nested1.indexOf('nested1a1') === -1).to.equal(false) + expect(og.nested.nested1.indexOf('nested1a2') === -1).to.equal(false) + expect(og.nested.nested1.indexOf('nested1a3') === -1).to.equal(false) + expect(og.nested.nested2.length).to.equal(3) + expect(og.nested.nested2.indexOf('nested2b1') === -1).to.equal(false) + expect(og.nested.nested2.indexOf('nested2b2') === -1).to.equal(false) + expect(og.nested.nested2.indexOf('nested2b3') === -1).to.equal(false) + expect(og.subnested.subnested1.length).to.equal(8) + expect(og.subnested.subnested1.indexOf('subnested1a1') === -1).to.equal(false) + expect(og.subnested.subnested1.indexOf('subnested1a2') === -1).to.equal(false) + expect(og.subnested.subnested1.indexOf('subnested1a3') === -1).to.equal(false) + expect(og.subnested.subnested1.indexOf('subnested1b1') === -1).to.equal(false) + expect(og.subnested.subnested1.indexOf('subnested1b2') === -1).to.equal(false) + expect(og.subnested.subnested1.indexOf('subnested1b3') === -1).to.equal(false) + // FIXME: Some work to be done still with deep-nesting of groups with the same name +}) +