From 3f1d5afb0b8f87494e91b8a80616da84c6624bc0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Feb 2023 00:51:27 +0000 Subject: [PATCH 01/36] chore(deps): bump http-cache-semantics from 4.1.0 to 4.1.1 Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/kornelski/http-cache-semantics/releases) - [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1) --- updated-dependencies: - dependency-name: http-cache-semantics dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index c6a6e3771c3..c7f72bb5499 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10897,9 +10897,9 @@ htmlparser2@^8.0.1: entities "^4.3.0" http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== http-errors@2.0.0: version "2.0.0" From 457b6f67212d69bbefa3e88bae4f9a2b99a8f726 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Feb 2023 00:53:24 +0000 Subject: [PATCH 02/36] chore(deps): bump unist-util-remove from 3.1.0 to 3.1.1 Bumps [unist-util-remove](https://github.com/syntax-tree/unist-util-remove) from 3.1.0 to 3.1.1. - [Release notes](https://github.com/syntax-tree/unist-util-remove/releases) - [Commits](https://github.com/syntax-tree/unist-util-remove/compare/3.1.0...3.1.1) --- updated-dependencies: - dependency-name: unist-util-remove dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- packages/rehype-highlight-lines/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/rehype-highlight-lines/package.json b/packages/rehype-highlight-lines/package.json index c01eb4c820c..f30cccff249 100644 --- a/packages/rehype-highlight-lines/package.json +++ b/packages/rehype-highlight-lines/package.json @@ -32,7 +32,7 @@ }, "peerDependencies": {}, "dependencies": { - "unist-util-remove": "3.1.0" + "unist-util-remove": "3.1.1" }, "devDependencies": {}, "files": [ diff --git a/yarn.lock b/yarn.lock index c6a6e3771c3..1133a490d3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19954,10 +19954,10 @@ unist-util-remove-position@^4.0.0: "@types/unist" "^2.0.0" unist-util-visit "^4.0.0" -unist-util-remove@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-3.1.0.tgz#8042577e151dac989b7517976bfe4bac58f76ccd" - integrity sha512-rO/sIghl13eN8irs5OBN2a4RC10MsJdiePCfwrvnzGtgIbHcDXr2REr0qi9F2r/CIb1r9FyyFmcMRIGs+EyUFw== +unist-util-remove@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-3.1.1.tgz#8bfa181aff916bd32a4ed30b3ed76d0c21c077df" + integrity sha512-kfCqZK5YVY5yEa89tvpl7KnBBHu2c6CzMkqHUrlOqaRgGOMp0sMvwWOVrbAtj03KhovQB7i96Gda72v/EFE0vw== dependencies: "@types/unist" "^2.0.0" unist-util-is "^5.0.0" From cd1eb78e0460827210dad2c827061baa7b9067d8 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 5 Feb 2023 00:58:59 +0000 Subject: [PATCH 03/36] [dependabot skip] bumped dependabot/npm_and_yarn/unist-util-remove-3.1.1 changes in config/dependencies.yaml --- config/dependencies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/dependencies.yaml b/config/dependencies.yaml index 002b686d496..07551e87844 100644 --- a/config/dependencies.yaml +++ b/config/dependencies.yaml @@ -154,7 +154,7 @@ rehype-jargon: 'hast-util-from-html': '1.0.1' rehype-highlight-lines: _: - 'unist-util-remove': '3.1.0' + 'unist-util-remove': '3.1.1' sandy: peer: '@freesewing/snapseries': *freesewing From 37ca7a759bfdff866c74ca8c844fc2038d0d0e22 Mon Sep 17 00:00:00 2001 From: Benjamin F Date: Thu, 23 Feb 2023 09:46:52 -0800 Subject: [PATCH 04/36] fix(workbench): Change forPrinting background fill to "none" --- sites/shared/components/workbench/layout/print/index.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sites/shared/components/workbench/layout/print/index.mjs b/sites/shared/components/workbench/layout/print/index.mjs index 8ec25143dfe..26833d9e7e9 100644 --- a/sites/shared/components/workbench/layout/print/index.mjs +++ b/sites/shared/components/workbench/layout/print/index.mjs @@ -35,7 +35,7 @@ export const PrintLayout = (props) => { } catch (err) { console.log(err, props.gist) } - const bgProps = { fill: 'url(#page)' } + const bgProps = { fill: 'none' } const exportIt = () => { setError(false) From e44d3ef34dcba84391fe4f2cef03090953c1398a Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Tue, 28 Feb 2023 12:04:36 -0600 Subject: [PATCH 05/36] refactor optiondocsimages script for mjs --- ...tiondocsimages.js => optiondocsimages.mjs} | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) rename scripts/{optiondocsimages.js => optiondocsimages.mjs} (68%) diff --git a/scripts/optiondocsimages.js b/scripts/optiondocsimages.mjs similarity index 68% rename from scripts/optiondocsimages.js rename to scripts/optiondocsimages.mjs index a234ede01cd..fa6a7efcdfe 100644 --- a/scripts/optiondocsimages.js +++ b/scripts/optiondocsimages.mjs @@ -10,17 +10,18 @@ * markdown/org/docs/patterns/[pattern]/[option]/[pattern]_[option]_sample.svg * */ -const fs = require('fs') -const path = require('path') -const core = require('../packages/core/dist') -const pi = require('../packages/pattern-info/dist') -const models = require('../packages/models/dist') -const wb32 = models.withBreasts.size32 -const noVersions = require('../plugins/plugin-versionfree-svg') -let { capitalize } = require('../packages/core/src/utils.mjs') -capitalize = capitalize.default -let theme = require('../plugins/plugin-theme/dist') -theme = theme.default +import fs from 'fs' +import path from 'path' +import { fileURLToPath } from 'url' +import { designs } from '../config/software/index.mjs' +import { cisFemaleAdult32 } from '../packages/models/dist/index.mjs' +import { plugin as noVersions } from '../plugins/plugin-versionfree-svg/dist/index.mjs' +import { capitalize } from '../packages/core/src/utils.mjs' +import { plugin as theme } from '../plugins/plugin-theme/dist/index.mjs' + +// when dependabot updates a dependency in a package.json, we want to update it in our dependencies.yaml +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) const image = (pattern, option) => ` @@ -34,13 +35,13 @@ const insertImage = (file, pattern, option) => { fs.writeFileSync(file, md + image(pattern, option)) } -const createImages = () => { - for (const pattern of pi.list) { +const createImages = async () => { + for (const pattern in designs) { if (true || pattern === 'unice') { - const Pattern = require(`../designs/${pattern}/dist/index.js`)[capitalize(pattern)] - for (const option of pi.options[pattern]) { + const Pattern = (await import(`../designs/${pattern}/dist/index.mjs`))[capitalize(pattern)] + for (const option in Pattern.patternConfig.options) { const p = new Pattern({ - measurements: wb32, + measurements: cisFemaleAdult32, settings: { idPrefix: `${pattern}_${option}`, embed: true, @@ -48,6 +49,8 @@ const createImages = () => { }) .use(theme) .use(noVersions) + .__init() + const file = path.join( 'markdown', 'org', From e923794051210d97a09ad665b9ce8a17aa8e8da9 Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Tue, 28 Feb 2023 12:46:43 -0600 Subject: [PATCH 06/36] lint fix --- scripts/optiondocsimages.mjs | 86 ++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/scripts/optiondocsimages.mjs b/scripts/optiondocsimages.mjs index fa6a7efcdfe..fd6ce9bd4fa 100644 --- a/scripts/optiondocsimages.mjs +++ b/scripts/optiondocsimages.mjs @@ -37,52 +37,50 @@ const insertImage = (file, pattern, option) => { const createImages = async () => { for (const pattern in designs) { - if (true || pattern === 'unice') { - const Pattern = (await import(`../designs/${pattern}/dist/index.mjs`))[capitalize(pattern)] - for (const option in Pattern.patternConfig.options) { - const p = new Pattern({ - measurements: cisFemaleAdult32, - settings: { - idPrefix: `${pattern}_${option}`, - embed: true, - }, - }) - .use(theme) - .use(noVersions) - .__init() + const Pattern = (await import(`../designs/${pattern}/dist/index.mjs`))[capitalize(pattern)] + for (const option in Pattern.patternConfig.options) { + const p = new Pattern({ + measurements: cisFemaleAdult32, + settings: { + idPrefix: `${pattern}_${option}`, + embed: true, + }, + }) + .use(theme) + .use(noVersions) + .__init() - const file = path.join( - 'markdown', - 'org', - 'docs', - 'patterns', - pattern, - 'options', - option.toLowerCase(), - `${pattern}_${option.toLowerCase()}_sample.svg` - ) - try { - const svg = p.sampleOption(option).render() - fs.writeFileSync(path.join(__dirname, '..', file), svg) - insertImage( - path.join( - 'markdown', - 'org', - 'docs', - 'patterns', - pattern, - 'options', - option.toLowerCase(), - 'en.md' - ), + const file = path.join( + 'markdown', + 'org', + 'docs', + 'patterns', + pattern, + 'options', + option.toLowerCase(), + `${pattern}_${option.toLowerCase()}_sample.svg` + ) + try { + const svg = p.sampleOption(option).render() + fs.writeFileSync(path.join(__dirname, '..', file), svg) + insertImage( + path.join( + 'markdown', + 'org', + 'docs', + 'patterns', pattern, - option - ) - console.log('✅ ' + file) - } catch (err) { - console.log('⚠️ ' + file) - console.log(err) - } + 'options', + option.toLowerCase(), + 'en.md' + ), + pattern, + option + ) + console.log('✅ ' + file) + } catch (err) { + console.log('⚠️ ' + file) + console.log(err) } } } From 44b0a9c9e5e7ac1ae064627553baf16c8a3ce91e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Feb 2023 18:56:47 +0000 Subject: [PATCH 07/36] chore(deps-dev): bump prettier from 2.8.3 to 2.8.4 Bumps [prettier](https://github.com/prettier/prettier) from 2.8.3 to 2.8.4. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/2.8.3...2.8.4) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- sites/sanity/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sites/sanity/package.json b/sites/sanity/package.json index c4624d57133..75eb27dc992 100644 --- a/sites/sanity/package.json +++ b/sites/sanity/package.json @@ -31,7 +31,7 @@ "devDependencies": { "@sanity/eslint-config-studio": "2.0.1", "eslint": "8.34.0", - "prettier": "2.8.3", + "prettier": "2.8.4", "typescript": "4.9.5", "@sanity/cli": "3.2.6" }, diff --git a/yarn.lock b/yarn.lock index 9216cda94f3..f13943ed819 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16124,10 +16124,10 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA== -prettier@2.8.3, prettier@^2.7.1: - version "2.8.3" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.3.tgz#ab697b1d3dd46fb4626fbe2f543afe0cc98d8632" - integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== +prettier@2.8.4, prettier@^2.7.1: + version "2.8.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" + integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== pretty-ms@^7.0.1: version "7.0.1" From 7012b6ab9a0ca9217ed92b998fadce249737616e Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 28 Feb 2023 18:59:47 +0000 Subject: [PATCH 08/36] [dependabot skip] chore(deps): bumped dependabot/npm_and_yarn/prettier-2.8.4 changes in config/dependencies.yaml --- config/dependencies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/dependencies.yaml b/config/dependencies.yaml index f18ddfcee72..c10d2f49784 100644 --- a/config/dependencies.yaml +++ b/config/dependencies.yaml @@ -356,7 +356,7 @@ sanity: dev: '@sanity/eslint-config-studio': '2.0.1' 'eslint': *eslint - 'prettier': '2.8.3' + 'prettier': '2.8.4' 'typescript': '4.9.5' '@sanity/cli': '3.2.6' From adb38168ed776c1e49c565f5c9e7f46a3f5ab7c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Mar 2023 05:01:59 +0000 Subject: [PATCH 09/36] chore(deps): bump react-instantsearch-dom from 6.38.3 to 6.39.0 Bumps [react-instantsearch-dom](https://github.com/algolia/instantsearch.js) from 6.38.3 to 6.39.0. - [Release notes](https://github.com/algolia/instantsearch.js/releases) - [Commits](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-dom@6.38.3...react-instantsearch-dom@6.39.0) --- updated-dependencies: - dependency-name: react-instantsearch-dom dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- sites/dev/package.json | 2 +- sites/lab/package.json | 2 +- sites/org/package.json | 2 +- yarn.lock | 30 +++++++++++++++--------------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/sites/dev/package.json b/sites/dev/package.json index 368b99e05ab..443fbbb7d7e 100644 --- a/sites/dev/package.json +++ b/sites/dev/package.json @@ -41,7 +41,7 @@ "react-copy-to-clipboard": "5.1.0", "react-dom": "18.2.0", "react-hotkeys-hook": "4.3.2", - "react-instantsearch-dom": "6.38.3", + "react-instantsearch-dom": "6.39.0", "react-markdown": "8.0.5", "react-swipeable": "7.0.0", "react-timeago": "7.1.0", diff --git a/sites/lab/package.json b/sites/lab/package.json index d3a60562d75..cc81ce16825 100644 --- a/sites/lab/package.json +++ b/sites/lab/package.json @@ -46,7 +46,7 @@ "react-copy-to-clipboard": "5.1.0", "react-hotkeys-hook": "4.3.2", "react-i18next": "12.1.4", - "react-instantsearch-dom": "6.38.3", + "react-instantsearch-dom": "6.39.0", "react-markdown": "8.0.5", "react-swipeable": "7.0.0", "react-timeago": "7.1.0", diff --git a/sites/org/package.json b/sites/org/package.json index 872dd549cc3..a15f9869b22 100644 --- a/sites/org/package.json +++ b/sites/org/package.json @@ -43,7 +43,7 @@ "next": "13.1.6", "react-dropzone": "14.2.3", "react-hotkeys-hook": "4.3.2", - "react-instantsearch-dom": "6.38.3", + "react-instantsearch-dom": "6.39.0", "react-hot-toast": "2.4.0", "react-markdown": "8.0.5", "react-swipeable": "7.0.0", diff --git a/yarn.lock b/yarn.lock index 9216cda94f3..393e3a940ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5556,10 +5556,10 @@ ajv@^8.11.0: require-from-string "^2.0.2" uri-js "^4.2.2" -algoliasearch-helper@^3.11.2: - version "3.11.2" - resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.11.2.tgz#f42db10433e6264f1d1ba503699cbdbff7b48dff" - integrity sha512-eKvSM5hz5w9RcUowu8LnQ5v0KRrFLCvF4K3KF/Ab3VwCT726rWgZUWUIQUPjr9qDENUMukQ/IHZ7bGUVYRGP0g== +algoliasearch-helper@^3.11.3: + version "3.11.3" + resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.11.3.tgz#6e7af8afe6f9a9e55186abffb7b6cf7ca8de3301" + integrity sha512-TbaEvLwiuGygHQIB8y+OsJKQQ40+JKUua5B91X66tMUHyyhbNHvqyr0lqd3wCoyKx7WybyQrC0WJvzoIeh24Aw== dependencies: "@algolia/events" "^4.0.1" @@ -16554,27 +16554,27 @@ react-i18next@12.1.4: "@babel/runtime" "^7.20.6" html-parse-stringify "^3.0.1" -react-instantsearch-core@6.38.3: - version "6.38.3" - resolved "https://registry.yarnpkg.com/react-instantsearch-core/-/react-instantsearch-core-6.38.3.tgz#aea403bdc36667180cf4c00c8af58102d1c56083" - integrity sha512-m7sL8NR5SlqctZJqoRYyBUFSUplK221EHESC+g6+fZ9Vd1fUX07MNvnbZ9+8Uyn0v+Hi8nL/3qGesJZ/xbJ/dg== +react-instantsearch-core@6.39.0: + version "6.39.0" + resolved "https://registry.yarnpkg.com/react-instantsearch-core/-/react-instantsearch-core-6.39.0.tgz#4b37e764677589d4640c90b7fad9eb4b0cbf3877" + integrity sha512-UROe0jjXI56pu1uHuP5lDhyXMIrodkDBISBv5pLU9/1LIYMNF3o4C9FllTdfia3Jz3lDDsSEcPpc/77so2x3Ww== dependencies: "@babel/runtime" "^7.1.2" - algoliasearch-helper "^3.11.2" + algoliasearch-helper "^3.11.3" prop-types "^15.6.2" react-fast-compare "^3.0.0" -react-instantsearch-dom@6.38.3: - version "6.38.3" - resolved "https://registry.yarnpkg.com/react-instantsearch-dom/-/react-instantsearch-dom-6.38.3.tgz#6e76b9781fda349dc56b086c12de25a4987a95c0" - integrity sha512-1VQxMq23pAaeJjyBdTuC+U2DHa35ygnKd4fCTWHv0YK/GwneoSOubxKf24QtNLsHfswJ/qjpW21xXv6IVRA90g== +react-instantsearch-dom@6.39.0: + version "6.39.0" + resolved "https://registry.yarnpkg.com/react-instantsearch-dom/-/react-instantsearch-dom-6.39.0.tgz#4d42efd4f92de14437e57c9a66c01e0fde92d5ae" + integrity sha512-wdFGDFugoCpO9buxjybyLTs7cblQ6g+NIiYIsZ9ua0FLUIdoeawXQYDV83dDzcNgbml5RTiKJsAFxlne8gguMg== dependencies: "@babel/runtime" "^7.1.2" - algoliasearch-helper "^3.11.2" + algoliasearch-helper "^3.11.3" classnames "^2.2.5" prop-types "^15.6.2" react-fast-compare "^3.0.0" - react-instantsearch-core "6.38.3" + react-instantsearch-core "6.39.0" react-is@18.2.0, react-is@^18.0.0, react-is@^18.2.0: version "18.2.0" From 6416feba1053b8c9f836e5e4445511ca66df3366 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 1 Mar 2023 05:05:34 +0000 Subject: [PATCH 10/36] [dependabot skip] chore(deps): bumped dependabot/npm_and_yarn/react-instantsearch-dom-6.39.0 changes in config/dependencies.yaml --- config/dependencies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/dependencies.yaml b/config/dependencies.yaml index f18ddfcee72..1c409090ea5 100644 --- a/config/dependencies.yaml +++ b/config/dependencies.yaml @@ -247,7 +247,7 @@ dev: 'react-copy-to-clipboard': &reactCopyToClipboard '5.1.0' 'react-dom': *react 'react-hotkeys-hook': &reactHotkeysHook '4.3.2' - 'react-instantsearch-dom': &reactInstantsearchDom '6.38.3' + 'react-instantsearch-dom': &reactInstantsearchDom '6.39.0' 'react-markdown': &reactMarkdown '8.0.5' 'react-swipeable': &reactSwipeable '7.0.0' 'react-timeago': &reactTimeago '7.1.0' From 6c7515317a9b438b9f24aeaa926c06a3be44164a Mon Sep 17 00:00:00 2001 From: Joost De Cock Date: Wed, 1 Mar 2023 22:05:49 +0100 Subject: [PATCH 11/36] New translations en.md (German) --- markdown/org/docs/patterns/aaron/instructions/de.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/markdown/org/docs/patterns/aaron/instructions/de.md b/markdown/org/docs/patterns/aaron/instructions/de.md index 40f2d1f11e3..3d7215e6c95 100644 --- a/markdown/org/docs/patterns/aaron/instructions/de.md +++ b/markdown/org/docs/patterns/aaron/instructions/de.md @@ -38,26 +38,26 @@ Das unten beschriebene Verfahren gilt sowohl für die Armlöcher als auch für d Ich würde dir anraten, mit der Öffnung zu beginnen, die die einheitlichste Krümmung hat. Standardmäßig sind dies die Armlöcher, aber wenn du die Schnittmusteroptionen geändert hast (zum Beispiel zu einem Racerback), ist dies möglicherweise nicht der Fall. -So oder so, je weniger scharfe Kurven du hast, desto einfacher wird es sein. Also beginnen Sie mit dem Aufwärmen, bevor Sie die dreckigen Kurven machen. +So oder so, je weniger scharfe Kurven du hast, desto einfacher wird es sein. Beginne also mit diesen, um dich an das Verfahren zu gewöhnen, bevor du dich an die komplexeren Stellen wagst. -We are going to finish the arm and neck hole with [knit binding](/docs/sewing/knit-binding) (note: not a knit band. Es gibt einen Unterschied, und der wird [hier](/docs/sewing/knit-binding) erklärt). +Wir werden die Armlöcher und die Halsöffnung mit einem [Einfassband](/docs/sewing/knit-binding) versäubern (Anmerkung: kein Bündchen. Es gibt einen Unterschied, und der wird [hier](/docs/sewing/knit-binding) erklärt). -Dies ist der einzige Teil, um dieses A-Shirt zu machen, das ein bisschen Praxis erfordert. Keine Sorge, alles was du tun musst, ist ein paar davon zu machen und du wirst in kürzester Zeit ein Profi sein. +Dies ist der einzige Schritt beim Anfertigen dieses A-Shirts, der etwas Übung erfordert. Keine Sorge, du musst das nur ein paar Mal gemacht haben und du wirst in kürzester Zeit ein Profi sein. -### Wähle deinen Platz +### Wähle eine Stelle ![Einfassung annähen](step03a.png) -The first thing we need to do is decide where we are going to start/stop our binding. Ich schlage Folgendes vor: +Das Erste, was wir tun müssen, ist, zu entscheiden, wo wir unser Einfassband ansetzen bzw. stoppen wollen. Ich schlage Folgendes vor: - Für die Armlöcher: an der Seitennaht. Dies wird unsere Naht unter dem Arm verbergen -- Für die Halsöffnung: die Mitte des Halsrückens. Möglicherweise möchten Sie dies mit einem Pin markieren +- Für die Halsöffnung: die Mitte des Halsrückens. Möglicherweise möchtest du dies mit einer Stecknadel markieren ### Place (the start of) your binding From f529de6f71dc4789a8f4db4c0b76c7cd1a8b7b5c Mon Sep 17 00:00:00 2001 From: Joost De Cock Date: Wed, 1 Mar 2023 23:32:52 +0100 Subject: [PATCH 12/36] New translations en.md (German) --- .../docs/patterns/aaron/instructions/de.md | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/markdown/org/docs/patterns/aaron/instructions/de.md b/markdown/org/docs/patterns/aaron/instructions/de.md index 3d7215e6c95..57417ff31fe 100644 --- a/markdown/org/docs/patterns/aaron/instructions/de.md +++ b/markdown/org/docs/patterns/aaron/instructions/de.md @@ -59,47 +59,47 @@ Das Erste, was wir tun müssen, ist, zu entscheiden, wo wir unser Einfassband an - Für die Armlöcher: an der Seitennaht. Dies wird unsere Naht unter dem Arm verbergen - Für die Halsöffnung: die Mitte des Halsrückens. Möglicherweise möchtest du dies mit einer Stecknadel markieren -### Place (the start of) your binding +### Platziere den Beginn deines Bandes ![Place (the start of) your binding](step03b.png) -Put your A-shirt down with the good side up, and place your binding strip on top of it with the good side down (as in, good sides together). +Lege dein A-Shirt mit der guten Seite nach oben und platzieren das Einfassband mit der guten Seite nach unten (also gute Seiten zusammen). -Align the long edge of your strip with the edge of your fabric so the strip lies on top of the fabric (not in the opening). Place the corner on your starting point. +Lege die lange Kante deines Streifens an die Kante deines Stoffes, sodass der Streife auf dem Stoff liegt (und nicht in der Öffnung). Platziere die Ecke an deinem Startpunkt. Schiebe nun das Einfassband 1cm über den Anfangspunkt hinweg. Dieser Zentimeter macht das spätere Zusammennähen der beiden Enden möglich. -### Vergewissern Sie sich, dass Sie wissen, wo Sie nähen sollen +### Vergewissere dich, dass du weißt, wo du nähen möchtest ![Vergewissern Sie sich, dass Sie wissen, wo Sie nähen sollen](step03c.png) -Wir werden 1,5 cm von der Kante nähen. Seien Sie also vorsichtig, denn das ist nicht die reguläre Nahtzugabe. +Wir werden 1.5 cm von der Kante entfernt nähen. Sei also vorsichtig, denn dies ist nicht die reguläre Nahtzugabe. -Der einfache Weg, diese 1,5 cm Distanz zu halten, ist mit einer Nahtspur. -Oder machen Sie, was ich tue, platzieren Sie ein Stück Markierungsband auf Ihrer Nähmaschine und markieren Sie eine Linie auf 1,5 cm aus Ihrer Nadel. -Dann musst du nur den Rand deines Stoffes an dieser Linie angleichen. +Der einfachste Weg, diese 1.5 cm Distanz einzuhalten, ist mit einem Abstandhalter. +Oder mache, was ich tue: platziere ein Stück Klebeband auf deiner Nähmaschine und markiere eine Linie 1.5cm von deiner Nadel entfernt. +Dann musst du nur den Rand deines Stoffes an dieser Linie ausrichten. -Außerdem beginnen wir nicht bei unserem Start, sondern wir überspringen die ersten 3cm. Dieser lose Raum wird später benötigt, um die Enden unserer Einfassung zu verbinden. +Außerdem beginnen wir nicht bei unserem Startpunkt, sondern wir überspringen die ersten 3cm. Diesen Spielraum brauchen wir später um die Enden unserer Einfassung zu verbinden. -### Nähe die Einfassung fest +### Nähe das Einfassband fest ![Nähe die Einfassung fest](step03d.png) -Nähen Sie Ihre Einfassung an und dehnen Sie den Einfassstreifen, aber nicht den Hauptstoff. Vergewissern Sie sich, nicht die letzten 3cm zu nähen, sondern lassen Sie sie lose, so wie Sie es zu Beginn getan haben. +Nähe dein Einfassband fest und dehne ihn dabei, ohne den Hauptstoff zu dehnen. Pass dabei auf, die letzten 3cm nicht festzunähen, sondern lasse diese genauso übrig wie am Anfang. -You might need to practice a bit to get the hang of it. +Du wirst etwas Übung brauchen, um den Dreh rauszubekommen. -> Achten Sie darauf, Ihre Bindung mehr zu strecken, wenn Sie eine schärfere Kurve nehmen. Lesen Sie meine Notizen auf [Strickbindung](/docs/sewing/knit-binding) für weitere Informationen. +> Achte darauf, deinen Einfassstreifen mehr zu dehnen, wenn du eine schärfere Kurve nähst. Lies meine Anmerkungen zu [Einfassbändern](/docs/sewing/knit-binding) für weitere Informationen. -### Bindungsende markieren +### Ende der Einfassung markieren ![Verbinden der Enden der Einfassung](step03e.png) -Es ist an der Zeit, die Enden der Bindung zusammen zu nähen, bevor wir unseren Kreis vollenden. +Es ist an der Zeit, die Enden des Bandes zusammenzunähen, bevor wir unseren Kreis mit den letzten 6cm vollenden. ![Markieren der Enden der Einfassung](step03f.png) @@ -107,19 +107,19 @@ Nimm eins der Enden und dehne es entlang der 3cm ab den Startpunkt, wie du es be Markiere die Einfassung, wo sie den Startpunkt erreicht. Tu das Gleiche für das andere Ende. -### Nähbindung endet zusammen +### Nähe den Einfassstreifen zusammen ![Zusammenfügen des Einfassstreifens](step03g.png) Falte dein A-Shirt so, dass es am einfachsten ist die guten Enden des Einfassbandes mit rechts auf rechts an der Markierung zusammenzubringen. Nähe die Einfassstreifen auf der Markierung zusammen. -> 6 cm ist nicht viel, aber sollte ausreichen, um beide Kanten bequem unter Ihrer Nähmaschine zu nähen. +> 6 cm sind nicht viel, aber sollte ausreichen, um beide Enden bequem mit der Nähmaschine zusammen zu nähen. ### Beende die letzten 6 cm Bindung ![Nähe die Enden der Einfassung zusammen](step03h.png) -Now that your binding ends are joined together, it's time to finish the last 6cm of binding. Sew it down, staying 1.5cm from the edge as you did before. +Jetzt wo die Enden deines Einfassbands verbunden sind, ist es an der Zeit, die letzten 6cm zu nähen. Nähe das Band fest, mit 1.5cm Abstand zur Kante wie bisher. ## Schritt 4: Falte den Einfassstreifen nach hinten und nähe ihn fest. @@ -127,15 +127,15 @@ Now that your binding ends are joined together, it's time to finish the last 6cm ![Falte den Einfassstreifen zurück](step04a.png) -Falte den Einfassstreifen um den Hauptstoff deines A-shirts nach hinten. So nähen wir es jetzt fest. +Falte den Einfassstreifen um den Hauptstoff deines A-Shirts nach hinten. So nähen wir es jetzt fest. -> Während der Stoff doppelt geklappt ist (Verstecke den Stoffrand im Prozess), ist das auf der Rückseite nicht nötig. Wir werden später nur noch den Rand zurückschneiden, da der Strick nicht schwenkt. Wenn wir den Stoff auch auf der Rückseite zurückklappen würden, würde er nur noch mehr massieren. +> Während der Stoff doppelt geklappt ist (Verstecke den Stoffrand im Prozess), ist das auf der Rückseite nicht nötig. Wir werden später nur noch den Rand zurückschneiden, da der Strickstoff nicht ausfranst. Wenn wir den Stoff auch auf der Rückseite umschlagen würden, würde es zu dick werden. -### Nähen Sie Ihre Bindung +### Nähe dein Einfassband fest ![Nähe den Einfassstreifen fest](step04b.png) -From the right side of your fabric, sew along the inner edge of your binding (furthest from the edge), making sure to catch the binding at the back in the process. +Von der rechten Seite des Stoffes nähst du entlang der Innenkante deines Einfassbandes entlang (am weitesten von der Kante entfernt), während du darauf achtest, das Band auf der Rückseite mitzunähen. @@ -143,23 +143,23 @@ Wenn Sie eine Coverlock Maschine haben, wäre das perfekt für diese Naht -Du musst deine Einfassung noch einmal etwas dehnen, während du dies tust. Dieses mal gibt es eine zusätzliche Sache zu beachten. +Du musst deine Einfassung noch einmal etwas dehnen, während du dies tust. Dieses Mal gibt es eine zusätzliche Sache zu beachten. -##### Vorsicht vor dem ungleichemäßigen Transport +##### Vorsicht vor dem ungleichmäßigen Transport Während du deine Einfassung annähst, zieht der Transporter der Nähmaschine die untere Schicht (also den Einfassstreifen unten) schneller vorwärts. -In einer perfekten Welt folgen alle Ebenen reibungslos. -In den meisten Fällen hinkt die obere Ebene (vor der Bindung) ein bisschen hinterher. -Dadurch wird Ihre Bindung nicht sauber am Stoffrand gefaltet, sondern hässliche Falten erzeugt. +In einer perfekten Welt werden alle Stofflagen gleichmäßig transportiert. +In den meisten Fällen hinkt die obere Stofflage (vordere Seite deiner Einfassung) ein bisschen hinterher. +Dadurch legt sich das Einfassband nicht sauber um den Stoffrand, sondern erzeugt hässliche Falten. -Also achte darauf, und wenn du das siehst, strecke die Unterschicht ein bisschen extra um sie zu kompensieren. +Also achte darauf, und wenn du das siehst, dehne die untere Schicht ein bisschen mehr um das zu kompensieren. -## Schritt 5: Strickbindung nach innen trimmen +## Schritt 5: Einfassstreifen innen einkürzen ![Schneide die Einfassung auf der Innenseite zurück](step05.png) From 43300883bed7671a1ef5b38fa41d023b6f9ecbad Mon Sep 17 00:00:00 2001 From: Joost De Cock Date: Wed, 1 Mar 2023 23:32:53 +0100 Subject: [PATCH 13/36] New translations en.md (German) --- markdown/org/docs/patterns/aaron/options/de.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markdown/org/docs/patterns/aaron/options/de.md b/markdown/org/docs/patterns/aaron/options/de.md index b63eea8df3f..b0a783752e2 100644 --- a/markdown/org/docs/patterns/aaron/options/de.md +++ b/markdown/org/docs/patterns/aaron/options/de.md @@ -1,5 +1,5 @@ - - - -title: "Aaron A-Shirt: Schnittmusteroptionen" +title: "Aaron, das A-Shirt: Schnittmusteroptionen" - - - From 4ee962fa6ac378a8f57578785b9faeff687d0a6d Mon Sep 17 00:00:00 2001 From: Joost De Cock Date: Wed, 1 Mar 2023 23:32:54 +0100 Subject: [PATCH 14/36] New translations en.md (German) --- markdown/org/docs/patterns/aaron/needs/de.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markdown/org/docs/patterns/aaron/needs/de.md b/markdown/org/docs/patterns/aaron/needs/de.md index 525735d57f6..1c16337bc09 100644 --- a/markdown/org/docs/patterns/aaron/needs/de.md +++ b/markdown/org/docs/patterns/aaron/needs/de.md @@ -13,7 +13,7 @@ Um Aaron zu erstellen, benötigst du Folgendes: Wie bei allen Strickwaren und Stretchgeweben wird dir ein Serger/Overlock das Leben erleichtern. -Wenn du keine/n hast, musst du aber nicht verzweifeln. Du brauchst sie nicht unbedingt. +Wenn du keine hast, musst du aber nicht verzweifeln. Du brauchst sie nicht unbedingt. Da die Seiten- und Schulternähte nicht gedehnt werden, kannst du sie mit einem gewöhnlichen Geradstich nähen. From 1481faf1e79bc37fb8ebd973c4101ee1876b002e Mon Sep 17 00:00:00 2001 From: Joost De Cock Date: Wed, 1 Mar 2023 23:32:55 +0100 Subject: [PATCH 15/36] New translations en.md (German) --- markdown/org/docs/patterns/aaron/cutting/de.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/markdown/org/docs/patterns/aaron/cutting/de.md b/markdown/org/docs/patterns/aaron/cutting/de.md index af2369573eb..f72fbefd4c1 100644 --- a/markdown/org/docs/patterns/aaron/cutting/de.md +++ b/markdown/org/docs/patterns/aaron/cutting/de.md @@ -1,10 +1,10 @@ - - - -title: "Aaron, A-Shirt: Zuschnitt" +title: "Aaron, das A-Shirt: Zuschnitt" - - - - Schneide **1 Rückenteil** im Stoffbruch - Schneide **1 Vorderteil** im Stoffbruch -- Schneide **3 Streifen** für Halsöffnung und Armlochbindung +- Schneide **3 Streifen** für Einfassband für Halsöffnung und Armloch From d18162969879c733f561c5b39724aa7c0e810552 Mon Sep 17 00:00:00 2001 From: Joost De Cock Date: Wed, 1 Mar 2023 23:32:56 +0100 Subject: [PATCH 16/36] New translations en.md (German) --- markdown/org/docs/patterns/aaron/de.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markdown/org/docs/patterns/aaron/de.md b/markdown/org/docs/patterns/aaron/de.md index fd0bc438e16..58229c9513c 100644 --- a/markdown/org/docs/patterns/aaron/de.md +++ b/markdown/org/docs/patterns/aaron/de.md @@ -1,5 +1,5 @@ - - - -title: "Aaron A-Shirt" +title: "Aaron, das A-Shirt" - - - From df214426c600070e09023ab0081d42cdc1874a0a Mon Sep 17 00:00:00 2001 From: Joost De Cock Date: Wed, 1 Mar 2023 23:32:57 +0100 Subject: [PATCH 17/36] New translations en.md (German) --- markdown/org/docs/patterns/aaron/fabric/de.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/markdown/org/docs/patterns/aaron/fabric/de.md b/markdown/org/docs/patterns/aaron/fabric/de.md index 15933e1f555..a11eda106e7 100644 --- a/markdown/org/docs/patterns/aaron/fabric/de.md +++ b/markdown/org/docs/patterns/aaron/fabric/de.md @@ -1,13 +1,13 @@ - - - -title: "Aaron A-Shirt: Stoffoptionen" +title: "Aaron, das A-Shirt: Stoffoptionen" - - - -Ein A-Shirt ist im Wesentlichen Unterwäsche. Und in dieser Hinsicht willst du etwas Bequemes, das sich auf der Haut gut anfühlt und atmungsaktiv ist. +Ein A-Shirt ist im wesentlichen Unterwäsche. Und in dieser Hinsicht willst du etwas Bequemes, das sich auf der Haut gut anfühlt und atmungsaktiv ist. -Baumwolle mit einem Hauch von etwas dehnbaren, vielleicht Viskose oder feiner Strick. Ich schlage vor, Sie gehen in den Stoffladen und fühlen sich um ein wenig. +Baumwolle mit einem Hauch von etwas Dehnbaren, vielleicht Viskose oder feiner Strick. Am besten gehst du in den Stoffladen und fühlst dich ein wenig um. -Dehnbarkeit ist zwar nicht erforderlich, aber für Unterwäsche sinnvoll. Allerdings können Sie dies in einem non-stretch machen und stellen Sie sicher, dass Sie sich locker oder lässig passen. +Dehnbarkeit ist zwar nicht erforderlich, aber für Unterwäsche sinnvoll. Allerdings kannst du Aaron auch aus einem nicht-dehnbaren Stoff nähen, nimm dann aber eine lockere oder lässige Passform. From 0f1cd0d9c772faccaad51c52cf9ce972db6e4d64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Mar 2023 04:58:32 +0000 Subject: [PATCH 18/36] chore(deps): bump pino from 8.8.0 to 8.11.0 Bumps [pino](https://github.com/pinojs/pino) from 8.8.0 to 8.11.0. - [Release notes](https://github.com/pinojs/pino/releases) - [Commits](https://github.com/pinojs/pino/compare/v8.8.0...v8.11.0) --- updated-dependencies: - dependency-name: pino dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- sites/backend/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sites/backend/package.json b/sites/backend/package.json index 2c26b632a71..c4027b85cc9 100644 --- a/sites/backend/package.json +++ b/sites/backend/package.json @@ -41,7 +41,7 @@ "passport": "0.6.0", "passport-http": "0.3.0", "passport-jwt": "4.0.1", - "pino": "8.8.0", + "pino": "8.11.0", "qrcode": "1.5.1", "swagger-ui-dist": "4.15.5", "swagger-ui-express": "4.6.0" diff --git a/yarn.lock b/yarn.lock index 9216cda94f3..24a052e4fcd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15655,10 +15655,10 @@ pino-std-serializers@^6.0.0: resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.0.0.tgz#4c20928a1bafca122fdc2a7a4a171ca1c5f9c526" integrity sha512-mMMOwSKrmyl+Y12Ri2xhH1lbzQxwwpuru9VjyJpgFIH4asSj88F2csdMwN6+M5g1Ll4rmsYghHLQJw81tgZ7LQ== -pino@8.8.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/pino/-/pino-8.8.0.tgz#1f0d6695a224aa06afc7ad60f2ccc4772d3b9233" - integrity sha512-cF8iGYeu2ODg2gIwgAHcPrtR63ILJz3f7gkogaHC/TXVVXxZgInmNYiIpDYEwgEkxZti2Se6P2W2DxlBIZe6eQ== +pino@8.11.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/pino/-/pino-8.11.0.tgz#2a91f454106b13e708a66c74ebc1c2ab7ab38498" + integrity sha512-Z2eKSvlrl2rH8p5eveNUnTdd4AjJk8tAsLkHYZQKGHP4WTh2Gi1cOSOs3eWPqaj+niS3gj4UkoreoaWgF3ZWYg== dependencies: atomic-sleep "^1.0.0" fast-redact "^3.1.1" From d07602b9d670076dac22927e7d540f34c92ba814 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 2 Mar 2023 05:01:38 +0000 Subject: [PATCH 19/36] [dependabot skip] chore(deps): bumped dependabot/npm_and_yarn/pino-8.11.0 changes in config/dependencies.yaml --- config/dependencies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/dependencies.yaml b/config/dependencies.yaml index f18ddfcee72..654e3287d08 100644 --- a/config/dependencies.yaml +++ b/config/dependencies.yaml @@ -216,7 +216,7 @@ backend: 'passport': '0.6.0' 'passport-http': '0.3.0' 'passport-jwt': '4.0.1' - 'pino': '8.8.0' + 'pino': '8.11.0' 'qrcode': '1.5.1' 'swagger-ui-dist': '4.15.5' 'swagger-ui-express': '4.6.0' From eed643b9cf86102571c32d1b817eda3c585aaa96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Mar 2023 04:58:05 +0000 Subject: [PATCH 20/36] chore(deps-dev): bump ava from 5.1.1 to 5.2.0 Bumps [ava](https://github.com/avajs/ava) from 5.1.1 to 5.2.0. - [Release notes](https://github.com/avajs/ava/releases) - [Commits](https://github.com/avajs/ava/compare/v5.1.1...v5.2.0) --- updated-dependencies: - dependency-name: ava dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 33da5502e8d..b81a376e71f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6028,9 +6028,9 @@ autoprefixer@10.4.13, autoprefixer@^10.4.0, autoprefixer@^10.4.13: postcss-value-parser "^4.2.0" ava@^5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/ava/-/ava-5.1.1.tgz#41b1e1f60965b0701aca3a48bc63cb4c94f7ee6d" - integrity sha512-od1CWgWVIKZSdEc1dhQWhbsd6KBs0EYjek7eqZNGPvy+NyC9Q1bXixcadlgOXwDG9aM0zLMQZwRXfe9gMb1LQQ== + version "5.2.0" + resolved "https://registry.yarnpkg.com/ava/-/ava-5.2.0.tgz#0fae0a987872df1859391a658f2374f5f16d6d29" + integrity sha512-W8yxFXJr/P68JP55eMpQIa6AiXhCX3VeuajM8nolyWNExcMDD6rnIWKTjw0B/+GkFHBIaN6Jd0LtcMThcoqVfg== dependencies: acorn "^8.8.1" acorn-walk "^8.2.0" From 098bab0388ae0f088dc769f04ef5a48134f03430 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Mar 2023 05:01:23 +0000 Subject: [PATCH 21/36] chore(deps): bump daisyui from 2.47.0 to 2.51.3 Bumps [daisyui](https://github.com/saadeghi/daisyui) from 2.47.0 to 2.51.3. - [Release notes](https://github.com/saadeghi/daisyui/releases) - [Changelog](https://github.com/saadeghi/daisyui/blob/master/CHANGELOG.md) - [Commits](https://github.com/saadeghi/daisyui/compare/v2.47.0...v2.51.3) --- updated-dependencies: - dependency-name: daisyui dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- sites/dev/package.json | 2 +- sites/lab/package.json | 2 +- sites/org/package.json | 2 +- sites/shared/package.json | 2 +- yarn.lock | 8 ++++---- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sites/dev/package.json b/sites/dev/package.json index 443fbbb7d7e..f81e2e25c20 100644 --- a/sites/dev/package.json +++ b/sites/dev/package.json @@ -32,7 +32,7 @@ "@next/bundle-analyzer": "13.1.6", "@tailwindcss/typography": "0.5.9", "algoliasearch": "4.14.3", - "daisyui": "2.47.0", + "daisyui": "2.51.3", "lodash.get": "4.4.2", "lodash.orderby": "4.6.0", "lodash.set": "4.3.2", diff --git a/sites/lab/package.json b/sites/lab/package.json index cc81ce16825..7f2c6df8f83 100644 --- a/sites/lab/package.json +++ b/sites/lab/package.json @@ -35,7 +35,7 @@ "d3-dispatch": "3.0.1", "d3-drag": "3.0.0", "d3-selection": "3.0.0", - "daisyui": "2.47.0", + "daisyui": "2.51.3", "i18next": "22.4.9", "lodash.get": "4.4.2", "lodash.orderby": "4.6.0", diff --git a/sites/org/package.json b/sites/org/package.json index a15f9869b22..ff2da27e527 100644 --- a/sites/org/package.json +++ b/sites/org/package.json @@ -35,7 +35,7 @@ "@tailwindcss/typography": "0.5.9", "algoliasearch": "4.14.3", "react-copy-to-clipboard": "5.1.0", - "daisyui": "2.47.0", + "daisyui": "2.51.3", "lodash.get": "4.4.2", "lodash.orderby": "4.6.0", "lodash.set": "4.3.2", diff --git a/sites/shared/package.json b/sites/shared/package.json index 40ab0676c3b..a4c06fcb6a9 100644 --- a/sites/shared/package.json +++ b/sites/shared/package.json @@ -23,7 +23,7 @@ "d3-dispatch": "3.0.1", "d3-drag": "3.0.0", "d3-selection": "3.0.0", - "daisyui": "2.47.0", + "daisyui": "2.51.3", "feed": "4.2.2", "file-saver": "2.0.5", "front-matter": "4.0.2", diff --git a/yarn.lock b/yarn.lock index 33da5502e8d..0f60efe6739 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7762,10 +7762,10 @@ d3-selection@3, d3-selection@3.0.0: resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31" integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== -daisyui@2.47.0: - version "2.47.0" - resolved "https://registry.yarnpkg.com/daisyui/-/daisyui-2.47.0.tgz#f3a8dad0838fc5cbd125910694fa2de0d34441bd" - integrity sha512-svZpXKldtHjXTEdj/lu2n7b+EQJSatqvmVB59k4dhCDOYUhUZ3jtGuPrgOJlPysHhDjxjCRWWug/fgV5e8tc/w== +daisyui@2.51.3: + version "2.51.3" + resolved "https://registry.yarnpkg.com/daisyui/-/daisyui-2.51.3.tgz#95dd0444f27f55326845db80076e76dcf8d31a92" + integrity sha512-AQa9exq/DsnvjyDi6bwOqHExQr9LJJag0iKRXNvRRtHXPo1gaAQ3ASJWylUB8J8KMH2M9zIpr7cvPHc7yGckyQ== dependencies: color "^4.2" css-selector-tokenizer "^0.8.0" From 0fa16c11fa2f49189ce4d207054fc904d7bc58e7 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 3 Mar 2023 05:04:14 +0000 Subject: [PATCH 22/36] [dependabot skip] chore(deps): bumped dependabot/npm_and_yarn/daisyui-2.51.3 changes in config/dependencies.yaml --- config/dependencies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/dependencies.yaml b/config/dependencies.yaml index f2fca46a38f..5bed07144f7 100644 --- a/config/dependencies.yaml +++ b/config/dependencies.yaml @@ -238,7 +238,7 @@ dev: '@next/bundle-analyzer': &next '13.1.6' '@tailwindcss/typography': &tailwindTypography '0.5.9' 'algoliasearch': '4.14.3' - 'daisyui': &daisyui '2.47.0' + 'daisyui': &daisyui '2.51.3' 'lodash.get': *_get 'lodash.orderby': &_orderby '4.6.0' 'lodash.set': *_set From 7edb0459162bd2e513c254392695505bc61d431c Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Mon, 20 Feb 2023 06:08:07 +0200 Subject: [PATCH 23/36] begin refactoring part resolution to handle one part at a time --- packages/core/src/pattern.mjs | 220 +++++++++------------- packages/core/tests/pattern-init.test.mjs | 63 ++++++- 2 files changed, 149 insertions(+), 134 deletions(-) diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index 109bd62987c..aa796240e95 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -13,7 +13,7 @@ import { version } from '../data.mjs' import { __loadPatternDefaults } from './config.mjs' import cloneDeep from 'lodash.clonedeep' -const DISTANCE_DEBUG = false +const DISTANCE_DEBUG = true ////////////////////////////////////////////// // CONSTRUCTOR // @@ -74,11 +74,12 @@ export function Pattern(designConfig) { * @param {object} part - The part to add * @return {object} this - The Pattern instance */ -Pattern.prototype.addPart = function (part) { +Pattern.prototype.addPart = function (part, runtime = false) { if (typeof part?.draft === 'function') { if (part.name) { this.designConfig.parts.push(part) - this.__initialized = false + if (runtime) { + } else this.__initialized = false } else this.store.log.error(`Part must have a name`) } else this.store.log.error(`Part must have a draft() method`) @@ -370,6 +371,7 @@ Pattern.prototype.use = function (plugin, data) { */ Pattern.prototype.__addDependency = function (name, part, dep) { this.__dependencies[name] = mergeDependencies(dep.name, this.__dependencies[name]) + // #FIXME What's supposed to happen here? if (typeof this.__designParts[dep.name] === 'undefined') { this.config = this.__addPartConfig(this.__designParts[dep.name]) } @@ -417,12 +419,6 @@ Pattern.prototype.__addPartMeasurements = function (part, list = false) { } } } - if (part.from) this.__addPartMeasurements(part.from, list) - if (part.after) { - if (Array.isArray(part.after)) { - for (const dep of part.after) this.__addPartMeasurements(dep, list) - } else this.__addPartMeasurements(part.after, list) - } // Weed out duplicates this.config.measurements = [...new Set(list)] @@ -452,12 +448,6 @@ Pattern.prototype.__addPartOptionalMeasurements = function (part, list = false) } } } - if (part.from) this.__addPartOptionalMeasurements(part.from, list) - if (part.after) { - if (Array.isArray(part.after)) { - for (const dep of part.after) this.__addPartOptionalMeasurements(dep, list) - } else this.__addPartOptionalMeasurements(part.after, list) - } // Weed out duplicates if (list.length > 0) this.config.optionalMeasurements = [...new Set(list)] @@ -475,37 +465,27 @@ Pattern.prototype.__addPartOptionalMeasurements = function (part, list = false) Pattern.prototype.__addPartOptions = function (part) { if (!this.config.options) this.config.options = {} if (part.options) { + const partDistance = this.__mutated.partDistance?.[part.name] || 0 for (const optionName in part.options) { - if (!this.__mutated.optionDistance[optionName]) { - this.__mutated.optionDistance[optionName] = this.__mutated.partDistance?.[part.name] || 0 + const optionDistance = this.__mutated.optionDistance[optionName] + if (!optionDistance) { + this.__mutated.optionDistance[optionName] = partDistance // Keep design parts immutable in the pattern or risk subtle bugs this.config.options[optionName] = Object.freeze(part.options[optionName]) this.store.log.debug(`🔵 __${optionName}__ option loaded from part \`${part.name}\``) } else { if (DISTANCE_DEBUG) this.store.log.debug( - 'optionDistance for ' + - optionName + - ' is ' + - this.__mutated.optionDistance[optionName] + - ', and partDistance for ' + - part.name + - ' is ' + - this.__mutated.partDistance[part.name] + `optionDistance for ${optionName} is ${optionDistance} and partDistance for ${part.name} is ${partDistance}` ) - if (this.__mutated.optionDistance[optionName] > this.__mutated.partDistance[part.name]) { + if (optionDistance > partDistance) { this.config.options[optionName] = part.options[optionName] + this.__mutated.optionDistance[optionName] = partDistance this.store.log.debug(`🟣 __${optionName}__ option overwritten by \`${part.name}\``) } } } } - if (part.from) this.__addPartOptions(part.from) - if (part.after) { - if (Array.isArray(part.after)) { - for (const dep of part.after) this.__addPartOptions(dep) - } else this.__addPartOptions(part.after) - } return this } @@ -1272,6 +1252,76 @@ Pattern.prototype.__resolveDraftOrder = function (graph = this.__resolvedDepende return this } +Pattern.prototype.__resolvePartMutation = function (part, dependency, depType) { + const current_part_distance = this.__mutated.partDistance[part.name] + const proposed_dependent_part_distance = current_part_distance + 1 + + this.__designParts[dependency.name] = Object.freeze(dependency) + switch (depType) { + case 'from': + this.__setFromHide(part, part.name, dependency.name) + this.__inject[part.name] = dependency.name + break + case 'after': + this.__setAfterHide(part, part.name, dependency.name) + this.__addDependency(part.name, part, dependency) + } + + if ( + typeof this.__mutated.partDistance[dependency.name] === 'undefined' || + this.__mutated.partDistance[dependency.name] < proposed_dependent_part_distance + ) { + this.__mutated.partDistance[dependency.name] = proposed_dependent_part_distance + if (DISTANCE_DEBUG) + this.store.log.debug( + `"${depType}:" partDistance for ${dependency.name} is ${ + this.__mutated.partDistance[dependency.name] + }` + ) + } +} + +Pattern.prototype.__resolvePart = function (part, distance = 0) { + if (distance === 0) { + this.__designParts[part.name] = Object.freeze(part) + } + let count = Object.keys(this.__designParts).length + distance++ + if (typeof this.__mutated.partDistance[part.name] === 'undefined') { + this.__mutated.partDistance[part.name] = distance + + if (DISTANCE_DEBUG) + this.store.log.debug( + `Base partDistance for ${part.name} is ${this.__mutated.partDistance[part.name]}` + ) + } + + // Hide when hideAll is set + if (part.hideAll) { + this.__mutated.partHide[part.name] = true + } + + // Resolve part mutations. first from then after + ;['from', 'after'].forEach((d) => { + if (part[d]) { + if (DISTANCE_DEBUG) this.store.log.debug(`Processing ${part.name} "${d}:"`) + + const depsOfType = Array.isArray(part[d]) ? part[d] : [part[d]] + + depsOfType.forEach((dot) => { + this.__resolvePartMutation(part, dot, d) + const newCount = Object.keys(this.__designParts).length + if (count < newCount) { + this.__resolvePart(dot, distance) + count = newCount + } + }) + } + }) + + // add the part's config + this.__addPartConfig(part) +} /** * Resolves parts and their dependencies * @@ -1281,113 +1331,21 @@ Pattern.prototype.__resolveDraftOrder = function (graph = this.__resolvedDepende * @return {Pattern} this - The Pattern instance */ Pattern.prototype.__resolveParts = function (count = 0, distance = 0) { - if (count === 0) { - for (const part of this.designConfig.parts) { - // Keep design parts immutable in the pattern or risk subtle bugs - this.__designParts[part.name] = Object.freeze(part) - } - } - distance++ - if (DISTANCE_DEBUG) this.store.log.debug('Distance incremented to ' + distance) for (const part of this.designConfig.parts) { - if (typeof this.__mutated.partDistance[part.name] === 'undefined') { - this.__mutated.partDistance[part.name] = distance - if (DISTANCE_DEBUG) - this.store.log.debug( - 'Base partDistance for ' + part.name + ' is ' + this.__mutated.partDistance[part.name] - ) - } - } - for (const [name, part] of Object.entries(this.__designParts)) { - const current_part_distance = this.__mutated.partDistance[part.name] - const proposed_dependent_part_distance = current_part_distance + 1 - // Hide when hideAll is set - if (part.hideAll) this.__mutated.partHide[part.name] = true - // Inject (from) - if (part.from) { - if (DISTANCE_DEBUG) this.store.log.debug('Processing ' + part.name + ' "from:"') - this.__setFromHide(part, name, part.from.name) - this.__designParts[part.from.name] = part.from - this.__inject[name] = part.from.name - if ( - typeof this.__mutated.partDistance[part.from.name] === 'undefined' || - this.__mutated.partDistance[part.from.name] < proposed_dependent_part_distance - ) { - this.__mutated.partDistance[part.from.name] = proposed_dependent_part_distance - if (DISTANCE_DEBUG) - this.store.log.debug( - '"from:" partDistance for ' + - part.from.name + - ' is ' + - this.__mutated.partDistance[part.from.name] - ) - } - } - // Simple dependency (after) - if (part.after) { - if (DISTANCE_DEBUG) this.store.log.debug('Processing ' + part.name + ' "after:"') - if (Array.isArray(part.after)) { - for (const dep of part.after) { - this.__setAfterHide(part, name, dep.name) - this.__designParts[dep.name] = dep - this.__addDependency(name, part, dep) - if ( - typeof this.__mutated.partDistance[dep.name] === 'undefined' || - this.__mutated.partDistance[dep.name] < proposed_dependent_part_distance - ) { - this.__mutated.partDistance[dep.name] = proposed_dependent_part_distance - if (DISTANCE_DEBUG) - this.store.log.debug( - '"after:" partDistance for ' + - dep.name + - ' is ' + - this.__mutated.partDistance[dep.name] - ) - } - } - } else { - this.__setAfterHide(part, name, part.after.name) - this.__designParts[part.after.name] = part.after - this.__addDependency(name, part, part.after) - if ( - typeof this.__mutated.partDistance[part.after.name] === 'undefined' || - this.__mutated.partDistance[part.after.name] < proposed_dependent_part_distance - ) { - this.__mutated.partDistance[part.after.name] = proposed_dependent_part_distance - if (DISTANCE_DEBUG) - this.store.log.debug( - '"after:" partDistance for ' + - part.after.name + - ' is ' + - this.__mutated.partDistance[part.after.name] - ) - } - } - } - } - // Did we discover any new dependencies? - const len = Object.keys(this.__designParts).length - // If so, resolve recursively - if (len > count) { - if (DISTANCE_DEBUG) this.store.log.debug('Recursing...') - return this.__resolveParts(len, distance) + this.__resolvePart(part, distance) } + // Print final part distances. for (const part of this.designConfig.parts) { - let qualifier = '' - if (DISTANCE_DEBUG) qualifier = 'final ' + let qualifier = DISTANCE_DEBUG ? 'final' : '' this.store.log.debug( - '⚪️ `' + - part.name + - '` ' + - qualifier + - 'options priority is __' + - this.__mutated.partDistance[part.name] + - '__' + `⚪️ ${part.name} ${qualifier} options priority is __${ + this.__mutated.partDistance[part.name] + }__` ) } - for (const part of Object.values(this.__designParts)) this.__addPartConfig(part) + // for (const part of Object.values(this.__designParts)) this.__addPartConfig(part) return this } diff --git a/packages/core/tests/pattern-init.test.mjs b/packages/core/tests/pattern-init.test.mjs index d3d5bcfc56f..bebc5f09b97 100644 --- a/packages/core/tests/pattern-init.test.mjs +++ b/packages/core/tests/pattern-init.test.mjs @@ -111,7 +111,7 @@ describe('Pattern', () => { parts: [partC], }) const pattern = new Pattern() - pattern.draft() + pattern.__init() it('Pattern.__init() should resolve all measurements', () => { expect( @@ -153,8 +153,8 @@ describe('Pattern', () => { }) it('Pattern.__init() should set config data in the store', () => { - expect(pattern.setStores[0].get('data.name')).to.equal('test') - expect(pattern.setStores[0].get('data.version')).to.equal('1.2.3') + expect(pattern.store.get('data.name')).to.equal('test') + expect(pattern.store.get('data.version')).to.equal('1.2.3') }) it('Pattern.__init() should resolve dependencies', () => { @@ -181,6 +181,63 @@ describe('Pattern', () => { expect(pattern.config.draftOrder[2]).to.equal('test.partC') }) + it('Pattern.__init() should overwrite options from dependencies', () => { + const partD = { + name: 'test.partD', + from: partB, + options: { + optB: { deg: 25, min: 15, max: 45 }, + }, + draft: ({ part }) => part, + } + + const Pattern = new Design({ + data: { + name: 'test', + version: '1.2.3', + }, + parts: [partD], + }) + const pattern = new Pattern() + pattern.__init() + for (const [key, value] of Object.entries(partD.options.optB)) { + expect(pattern.config.options.optB[key]).to.equal(value) + } + }) + + it('Pattern.__init() should overwrite options from complex dependencies', () => { + const partD = { + name: 'test.partD', + from: partB, + options: { + optB: { deg: 25, min: 15, max: 45 }, + }, + draft: ({ part }) => part, + } + + const partE = { + name: 'test.partE', + from: partD, + options: { + optB: { deg: 10, min: 15, max: 50 }, + }, + draft: ({ part }) => part, + } + + const Pattern = new Design({ + data: { + name: 'test', + version: '1.2.3', + }, + parts: [partC, partE], + }) + const pattern = new Pattern() + pattern.__init() + for (const [key, value] of Object.entries(partE.options.optB)) { + expect(pattern.config.options.optB[key]).to.equal(value) + } + }) + // I am aware this does too much for one unit test, but this is to simplify TDD // we can split it up later it('Pattern.__init() should resolve nested injections', () => { From 0e08ed7014f596e83ca3871e34bbaa1fb98cbf37 Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Mon, 20 Feb 2023 23:59:28 +0200 Subject: [PATCH 24/36] all part resolution business happens on a per-part basis --- packages/core/src/pattern.mjs | 244 ++++++++-------------- packages/core/tests/pattern-init.test.mjs | 8 +- 2 files changed, 91 insertions(+), 161 deletions(-) diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index aa796240e95..e5c7ff9e25a 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -13,8 +13,7 @@ import { version } from '../data.mjs' import { __loadPatternDefaults } from './config.mjs' import cloneDeep from 'lodash.clonedeep' -const DISTANCE_DEBUG = true - +const DISTANCE_DEBUG = false ////////////////////////////////////////////// // CONSTRUCTOR // ////////////////////////////////////////////// @@ -74,12 +73,11 @@ export function Pattern(designConfig) { * @param {object} part - The part to add * @return {object} this - The Pattern instance */ -Pattern.prototype.addPart = function (part, runtime = false) { +Pattern.prototype.addPart = function (part) { if (typeof part?.draft === 'function') { if (part.name) { this.designConfig.parts.push(part) - if (runtime) { - } else this.__initialized = false + this.__initialized = false } else this.store.log.error(`Part must have a name`) } else this.store.log.error(`Part must have a draft() method`) @@ -369,14 +367,12 @@ Pattern.prototype.use = function (plugin, data) { * @param {object} dep - The dependency configuration * @return {object} this - The Pattern instance */ -Pattern.prototype.__addDependency = function (name, part, dep) { - this.__dependencies[name] = mergeDependencies(dep.name, this.__dependencies[name]) - // #FIXME What's supposed to happen here? - if (typeof this.__designParts[dep.name] === 'undefined') { - this.config = this.__addPartConfig(this.__designParts[dep.name]) - } - - return this +Pattern.prototype.__addDependency = function (dependencyList, part, dep) { + this[dependencyList][part.name] = this[dependencyList][part.name] || [] + if (dependencyList == '__resolvedDependencies' && DISTANCE_DEBUG) + this.store.log.debug(`add ${dep.name} to ${part.name} dependencyResolution`) + if (this[dependencyList][part.name].indexOf(dep.name) === -1) + this[dependencyList][part.name].push(dep.name) } /** @@ -476,7 +472,7 @@ Pattern.prototype.__addPartOptions = function (part) { } else { if (DISTANCE_DEBUG) this.store.log.debug( - `optionDistance for ${optionName} is ${optionDistance} and partDistance for ${part.name} is ${partDistance}` + `optionDistance for __${optionName}__ is __${optionDistance}__ and partDistance for \`${part.name}\` is __${partDistance}__` ) if (optionDistance > partDistance) { this.config.options[optionName] = part.options[optionName] @@ -552,6 +548,11 @@ Pattern.prototype.__addPartPlugins = function (part) { `Plugin \`${name}\` was requested conditionally, but is already added explicitly. Not loading.` ) } + // swap from a conditional if needed + else if (plugins[name].condition) { + plugins[name] = plugin + this.store.log.info(`Plugin \`${name}\` was explicitly added. Changing from conditional.`) + } } } @@ -741,21 +742,11 @@ Pattern.prototype.__isPartHidden = function (partName) { Pattern.prototype.__isStackHidden = function (stackName) { if (!this.stacks[stackName]) return true const parts = this.stacks[stackName].getPartNames() - if (Array.isArray(this.settings[this.activeStack || 0].only)) { - for (const partName of parts) { - if (this.settings[this.activeStack || 0].only.includes(partName)) return false - } - return true - } for (const partName of parts) { - if (this.__designParts?.[partName]?.hide) return true - if (this.__designParts?.[partName]?.hideAll) return true - if (this.__mutated.partHide?.[partName]) return true - if (this.__mutated.partHideAll?.[partName]) return true - if (this.parts?.[this.activeSet]?.[partName]?.hidden) return true + if (!this.__isPartHidden(partName)) return false } - return false + return true } /** @@ -1189,35 +1180,6 @@ Pattern.prototype.__pack = function () { return this } -/** - * Recursively solves part dependencies for a part - * - * @private - * @param {object} seen - Object to keep track of seen dependencies - * @param {string} part - Name of the part - * @param {object} graph - Dependency graph, used to call itself recursively - * @param {array} deps - List of dependencies - * @return {Array} deps - The list of dependencies - */ -Pattern.prototype.__resolveDependency = function ( - seen, - part, - graph = this.dependencies, - deps = [] -) { - if (typeof seen[part] === 'undefined') seen[part] = true - if (typeof graph[part] === 'string') graph[part] = [graph[part]] - if (Array.isArray(graph[part])) { - if (graph[part].length === 0) return [] - else { - if (deps.indexOf(graph[part]) === -1) deps.push(...graph[part]) - for (let apart of graph[part]) deps.concat(this.__resolveDependency(seen, apart, graph, deps)) - } - } - - return deps -} - /** * Resolves the draft order based on the configuation * @@ -1226,37 +1188,23 @@ Pattern.prototype.__resolveDependency = function ( * @return {Pattern} this - The Pattern instance */ Pattern.prototype.__resolveDraftOrder = function (graph = this.__resolvedDependencies) { - let sorted = [] - let visited = {} - Object.keys(graph).forEach(function visit(name, ancestors) { - if (!Array.isArray(ancestors)) ancestors = [] - ancestors.push(name) - visited[name] = true - if (typeof graph[name] !== 'undefined') { - graph[name].forEach(function (dep) { - if (visited[dep]) return - visit(dep, ancestors.slice(0)) - }) - } - if (sorted.indexOf(name) < 0) sorted.push(name) - }) - - // Don't forget about parts without dependencies - for (const part in this.__designParts) { - if (sorted.indexOf(part) === -1) sorted.push(part) - } - + const sorted = Object.keys(this.__designParts).sort( + (p1, p2) => this.__mutated.partDistance[p2] - this.__mutated.partDistance[p1] + ) this.__draftOrder = sorted this.config.draftOrder = sorted return this } -Pattern.prototype.__resolvePartMutation = function (part, dependency, depType) { - const current_part_distance = this.__mutated.partDistance[part.name] - const proposed_dependent_part_distance = current_part_distance + 1 +Pattern.prototype.__resolvePartDependencyChain = function (depChain, dependency, depType) { + const part = depChain[0] this.__designParts[dependency.name] = Object.freeze(dependency) + this.__addDependency('__dependencies', part, dependency) + + depChain.forEach((c) => this.__addDependency('__resolvedDependencies', c, dependency)) + switch (depType) { case 'from': this.__setFromHide(part, part.name, dependency.name) @@ -1264,35 +1212,72 @@ Pattern.prototype.__resolvePartMutation = function (part, dependency, depType) { break case 'after': this.__setAfterHide(part, part.name, dependency.name) - this.__addDependency(part.name, part, dependency) - } - - if ( - typeof this.__mutated.partDistance[dependency.name] === 'undefined' || - this.__mutated.partDistance[dependency.name] < proposed_dependent_part_distance - ) { - this.__mutated.partDistance[dependency.name] = proposed_dependent_part_distance - if (DISTANCE_DEBUG) - this.store.log.debug( - `"${depType}:" partDistance for ${dependency.name} is ${ - this.__mutated.partDistance[dependency.name] - }` - ) } } -Pattern.prototype.__resolvePart = function (part, distance = 0) { +Pattern.prototype.__resolveMutatedPartDistance = function (partName) { + const proposed_dependent_part_distance = this.__mutated.partDistance[partName] + 1 + let didChange = false + if (!this.__dependencies[partName]) return false + this.__dependencies[partName].forEach((dependency) => { + if ( + typeof this.__mutated.partDistance[dependency] === 'undefined' || + this.__mutated.partDistance[dependency] < proposed_dependent_part_distance + ) { + didChange = true + this.__mutated.partDistance[dependency] = proposed_dependent_part_distance + this.__resolveMutatedPartDistance(dependency) + } + if (DISTANCE_DEBUG) + this.store.log.debug( + `"${depType}:" partDistance for \`${dependency}\` is __${this.__mutated.partDistance[dependency]}__` + ) + }) + + return didChange +} + +const depTypes = ['from', 'after'] +Pattern.prototype.__resolvePartDependencies = function (depChain, distance) { + // Resolve part Dependencies. first from then after + const part = depChain[0] + this.__resolvedDependencies[part.name] = this.__resolvedDependencies[part.name] || [] + + depTypes.forEach((d) => { + if (part[d]) { + if (DISTANCE_DEBUG) this.store.log.debug(`Processing \`${part.name}\` "${d}:"`) + + const depsOfType = Array.isArray(part[d]) ? part[d] : [part[d]] + + depsOfType.forEach((dot) => { + let count = Object.keys(this.__designParts).length + // if any changes resulted from resolving this part mutation + this.__resolvePartDependencyChain(depChain, dot, d) + // if a new part was added, resolve the part + const newCount = Object.keys(this.__designParts).length + if (count < newCount) { + this.__resolvePart([dot, ...depChain], distance) + count = newCount + } + }) + } + }) + + this.__resolveMutatedPartDistance(part.name) +} + +Pattern.prototype.__resolvePart = function (depChain, distance = 0) { + const part = depChain[0] if (distance === 0) { this.__designParts[part.name] = Object.freeze(part) } - let count = Object.keys(this.__designParts).length distance++ if (typeof this.__mutated.partDistance[part.name] === 'undefined') { this.__mutated.partDistance[part.name] = distance if (DISTANCE_DEBUG) this.store.log.debug( - `Base partDistance for ${part.name} is ${this.__mutated.partDistance[part.name]}` + `Base partDistance for \`${part.name}\` is __${this.__mutated.partDistance[part.name]}__` ) } @@ -1301,23 +1286,7 @@ Pattern.prototype.__resolvePart = function (part, distance = 0) { this.__mutated.partHide[part.name] = true } - // Resolve part mutations. first from then after - ;['from', 'after'].forEach((d) => { - if (part[d]) { - if (DISTANCE_DEBUG) this.store.log.debug(`Processing ${part.name} "${d}:"`) - - const depsOfType = Array.isArray(part[d]) ? part[d] : [part[d]] - - depsOfType.forEach((dot) => { - this.__resolvePartMutation(part, dot, d) - const newCount = Object.keys(this.__designParts).length - if (count < newCount) { - this.__resolvePart(dot, distance) - count = newCount - } - }) - } - }) + this.__resolvePartDependencies(depChain, distance) // add the part's config this.__addPartConfig(part) @@ -1332,21 +1301,19 @@ Pattern.prototype.__resolvePart = function (part, distance = 0) { */ Pattern.prototype.__resolveParts = function (count = 0, distance = 0) { for (const part of this.designConfig.parts) { - this.__resolvePart(part, distance) + this.__resolvePart([part], distance) } // Print final part distances. for (const part of this.designConfig.parts) { let qualifier = DISTANCE_DEBUG ? 'final' : '' this.store.log.debug( - `⚪️ ${part.name} ${qualifier} options priority is __${ + `⚪️ \`${part.name}\` ${qualifier} options priority is __${ this.__mutated.partDistance[part.name] }__` ) } - // for (const part of Object.values(this.__designParts)) this.__addPartConfig(part) - return this } @@ -1354,35 +1321,10 @@ Pattern.prototype.__resolveParts = function (count = 0, distance = 0) { * Resolves parts depdendencies into a flat array * * @private - * @param {object} graph - The graph is used to call itsels recursively * @return {Pattern} this - The Pattern instance */ -Pattern.prototype.__resolveDependencies = function (graph = false) { - if (!graph) graph = this.__dependencies - for (const i in this.__inject) { - const dependency = this.__inject[i] - if (typeof this.__dependencies[i] === 'undefined') this.__dependencies[i] = dependency - else if (this.__dependencies[i] !== dependency) { - if (typeof this.__dependencies[i] === 'string') { - this.__dependencies[i] = [this.__dependencies[i], dependency] - } else if (Array.isArray(this.__dependencies[i])) { - if (this.__dependencies[i].indexOf(dependency) === -1) - this.__dependencies[i].push(dependency) - } else { - this.store.log.error('Part dependencies should be a string or an array of strings') - throw new Error('Part dependencies should be a string or an array of strings') - } - } - } - - let resolved = {} - let seen = {} - for (let part in graph) resolved[part] = this.__resolveDependency(seen, part, graph) - for (let part in seen) if (typeof resolved[part] === 'undefined') resolved[part] = [] - - this.__resolvedDependencies = resolved - this.config.resolvedDependencies = resolved - +Pattern.prototype.__resolveDependencies = function () { + this.config.resolvedDependencies = this.__resolvedDependencies return this } @@ -1563,19 +1505,7 @@ Pattern.prototype.__wants = function (partName, set = 0) { */ function mergeDependencies(dep = [], current = []) { // Current dependencies - const list = [] - if (Array.isArray(current)) list.push(...current) - else if (typeof current === 'string') list.push(current) + const list = [].concat(current, dep) - if (Array.isArray(dep)) list.push(...dep) - else if (typeof dep === 'string') list.push(dep) - - // Dependencies should be parts names (string) not the object - const deps = [] - for (const part of [...new Set(list)]) { - if (typeof part === 'object') deps.push(part.name) - else deps.push(part) - } - - return deps + return [...new Set(list)] } diff --git a/packages/core/tests/pattern-init.test.mjs b/packages/core/tests/pattern-init.test.mjs index bebc5f09b97..8f5c76621d7 100644 --- a/packages/core/tests/pattern-init.test.mjs +++ b/packages/core/tests/pattern-init.test.mjs @@ -324,10 +324,10 @@ describe('Pattern', () => { expect(pattern.config.options.optionR.list[1]).to.equal('green') expect(pattern.config.options.optionR.list[2]).to.equal('blue') // Dependencies - expect(pattern.__dependencies.partB[0]).to.equal('partA') - expect(pattern.__dependencies.partC[0]).to.equal('partB') - expect(pattern.__dependencies.partR[0]).to.equal('partC') - expect(pattern.__dependencies.partR[1]).to.equal('partA') + expect(pattern.__dependencies.partB).to.include('partA') + expect(pattern.__dependencies.partC).to.include('partB') + expect(pattern.__dependencies.partR).to.include('partC') + expect(pattern.__dependencies.partR).to.include('partA') // Inject expect(pattern.__inject.partB).to.equal('partA') expect(pattern.__inject.partC).to.equal('partB') From 6d20884ae3988df21ae6346d45632c3bbcf57269 Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Tue, 21 Feb 2023 16:17:57 +0200 Subject: [PATCH 25/36] add tests for runtime part adding --- packages/core/src/pattern.mjs | 7 +- .../core/tests/pattern-runtime-parts.test.mjs | 101 ++++++++++++++++++ 2 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 packages/core/tests/pattern-runtime-parts.test.mjs diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index e5c7ff9e25a..f9abd74be97 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -73,11 +73,14 @@ export function Pattern(designConfig) { * @param {object} part - The part to add * @return {object} this - The Pattern instance */ -Pattern.prototype.addPart = function (part) { +Pattern.prototype.addPart = function (part, resolveImmediately = false) { if (typeof part?.draft === 'function') { if (part.name) { this.designConfig.parts.push(part) - this.__initialized = false + if (resolveImmediately) { + this.store.log.debug(`Perfoming runtime resolution of new part ${part.name}`) + this.__resolvePart([part]) + } else this.__initialized = false } else this.store.log.error(`Part must have a name`) } else this.store.log.error(`Part must have a draft() method`) diff --git a/packages/core/tests/pattern-runtime-parts.test.mjs b/packages/core/tests/pattern-runtime-parts.test.mjs new file mode 100644 index 00000000000..a3a640e6721 --- /dev/null +++ b/packages/core/tests/pattern-runtime-parts.test.mjs @@ -0,0 +1,101 @@ +import chai from 'chai' +import { Design } from '../src/index.mjs' + +const expect = chai.expect + +describe('Pattern', () => { + describe('.addPart()', () => { + const part1 = { + name: 'test', + draft: ({ part }) => part, + } + + const part2 = { + name: 'test2', + from: part1, + draft: ({ part }) => part, + } + + const part3 = { + name: 'test3', + after: part2, + draft: ({ part }) => part, + } + + describe('with resolveImmediately: true', () => { + it('Should add the part to the internal part object', () => { + const design = new Design({ parts: [part1] }) + const pattern = new design() + pattern.__init() + pattern.addPart(part2, true) + expect(pattern.__designParts.test2).to.equal(part2) + }) + + it('Should resolve injected dependencies for the new part', () => { + const design = new Design({ parts: [part1] }) + const pattern = new design() + pattern.__init() + pattern.addPart(part2, true) + expect(pattern.__inject.test2).to.equal('test') + }) + + it('Should resolve all dependencies for the new part', () => { + const design = new Design({ parts: [part1] }) + const pattern = new design() + pattern.__init() + pattern.addPart(part3, true) + expect(pattern.config.resolvedDependencies.test3).to.have.members(['test', 'test2']) + expect(pattern.__designParts.test2).to.equal(part2) + }) + + it('Should add a the measurements for the new part', () => { + const design = new Design({ parts: [part1] }) + const pattern = new design() + pattern.__init() + + const part2 = { + name: 'test2', + measurements: ['neck'], + draft: ({ part }) => part, + } + + pattern.addPart(part2, true) + expect(pattern.config.measurements).to.include('neck') + }) + + it('Should add the plugins for the new part', () => { + const design = new Design({ parts: [part1] }) + const pattern = new design() + pattern.__init() + + const plugin = { name: 'testPlugin' } + const part2 = { + name: 'test2', + plugins: [plugin], + draft: ({ part }) => part, + } + + pattern.addPart(part2, true) + expect(pattern.config.plugins.testPlugin).to.equal(plugin) + }) + + it('Should add the options for the new part', () => { + const design = new Design({ parts: [part1] }) + const pattern = new design() + pattern.__init() + + const opt1 = { pct: 10, min: 0, max: 50 } + const part2 = { + name: 'test2', + options: { + opt1, + }, + draft: ({ part }) => part, + } + + pattern.addPart(part2, true) + expect(pattern.config.options.opt1).to.equal(opt1) + }) + }) + }) +}) From 863237ccfe9e240d4375b422708bb9e6ef9b56df Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Tue, 21 Feb 2023 22:33:57 +0200 Subject: [PATCH 26/36] move all pattern config resolution to separate class --- packages/core/src/pattern.mjs | 502 ++---------------- packages/core/src/patternConfig.mjs | 419 +++++++++++++++ packages/core/tests/pattern-init.test.mjs | 38 +- .../core/tests/pattern-runtime-parts.test.mjs | 71 ++- packages/core/tests/snap.test.mjs | 3 + 5 files changed, 534 insertions(+), 499 deletions(-) create mode 100644 packages/core/src/patternConfig.mjs diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index f9abd74be97..d593c360304 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -11,9 +11,9 @@ import { Store } from './store.mjs' import { Hooks } from './hooks.mjs' import { version } from '../data.mjs' import { __loadPatternDefaults } from './config.mjs' +import { PatternConfig, getPluginName } from './patternConfig.mjs' import cloneDeep from 'lodash.clonedeep' -const DISTANCE_DEBUG = false ////////////////////////////////////////////// // CONSTRUCTOR // ////////////////////////////////////////////// @@ -25,7 +25,7 @@ const DISTANCE_DEBUG = false * @param {object} config - The Design config * @return {object} this - The Pattern instance */ -export function Pattern(designConfig) { +export function Pattern(designConfig = {}) { // Non-enumerable properties __addNonEnumProp(this, 'plugins', {}) __addNonEnumProp(this, 'width', 0) @@ -39,19 +39,9 @@ export function Pattern(designConfig) { __addNonEnumProp(this, 'Attributes', Attributes) __addNonEnumProp(this, 'macros', {}) __addNonEnumProp(this, '__initialized', false) - __addNonEnumProp(this, '__designParts', {}) - __addNonEnumProp(this, '__inject', {}) - __addNonEnumProp(this, '__dependencies', {}) - __addNonEnumProp(this, '__resolvedDependencies', {}) - __addNonEnumProp(this, '__resolvedParts', []) + __addNonEnumProp(this, 'config.parts', {}) + __addNonEnumProp(this, 'config.resolvedDependencies', {}) __addNonEnumProp(this, '__storeMethods', new Set()) - __addNonEnumProp(this, '__mutated', { - optionDistance: {}, - partDistance: {}, - partHide: {}, - partHideAll: {}, - }) - __addNonEnumProp(this, '__draftOrder', []) __addNonEnumProp(this, '__hide', {}) // Enumerable properties @@ -59,6 +49,7 @@ export function Pattern(designConfig) { this.config = {} // Will hold the resolved pattern after calling __init() this.store = new Store() // Pattern-wide store this.setStores = [] // Per-set stores + __addNonEnumProp(this, '__configResolver', new PatternConfig(this)) // handles config resolution during __init() as well as runtime part adding return this } @@ -74,16 +65,11 @@ export function Pattern(designConfig) { * @return {object} this - The Pattern instance */ Pattern.prototype.addPart = function (part, resolveImmediately = false) { - if (typeof part?.draft === 'function') { - if (part.name) { - this.designConfig.parts.push(part) - if (resolveImmediately) { - this.store.log.debug(`Perfoming runtime resolution of new part ${part.name}`) - this.__resolvePart([part]) - } else this.__initialized = false - } else this.store.log.error(`Part must have a name`) - } else this.store.log.error(`Part must have a draft() method`) - + if (this.__configResolver.validatePart(part) && this.designConfig.parts.indexOf(part) === -1) { + this.designConfig.parts.push(part) + if (resolveImmediately) this.__configResolver.addPart(part) + else this.__initialized = false + } return this } @@ -132,15 +118,15 @@ Pattern.prototype.createPartForSet = function (partName, set = 0) { this.parts[set][partName] = this.__createPartWithContext(partName, set) // Handle inject/inheritance - if (typeof this.__inject[partName] === 'string') { + if (typeof this.config.inject[partName] === 'string') { this.setStores[set].log.debug( - `Creating part \`${partName}\` from part \`${this.__inject[partName]}\`` + `Creating part \`${partName}\` from part \`${this.config.inject[partName]}\`` ) try { - this.parts[set][partName].__inject(this.parts[set][this.__inject[partName]]) + this.parts[set][partName].__inject(this.parts[set][this.config.inject[partName]]) } catch (err) { this.setStores[set].log.error([ - `Could not inject part \`${this.__inject[partName]}\` into part \`${partName}\``, + `Could not inject part \`${this.config.inject[partName]}\` into part \`${partName}\``, err, ]) } @@ -161,11 +147,11 @@ Pattern.prototype.createPartForSet = function (partName, set = 0) { } Pattern.prototype.draftPartForSet = function (partName, set) { - if (typeof this.__designParts?.[partName]?.draft === 'function') { + if (typeof this.config.parts?.[partName]?.draft === 'function') { this.activePart = partName try { this.__runHooks('prePartDraft') - const result = this.__designParts[partName].draft(this.parts[set][partName].shorthand()) + const result = this.config.parts[partName].draft(this.parts[set][partName].shorthand()) this.__runHooks('postPartDraft') if (typeof result === 'undefined') { this.setStores[set].log.error( @@ -361,209 +347,6 @@ Pattern.prototype.use = function (plugin, data) { // PRIVATE METHODS // ////////////////////////////////////////////// -/** - * Adds a part as a simple dependency - * - * @private - * @param {string} name - The name of the dependency - * @param {object} part - The part configuration - * @param {object} dep - The dependency configuration - * @return {object} this - The Pattern instance - */ -Pattern.prototype.__addDependency = function (dependencyList, part, dep) { - this[dependencyList][part.name] = this[dependencyList][part.name] || [] - if (dependencyList == '__resolvedDependencies' && DISTANCE_DEBUG) - this.store.log.debug(`add ${dep.name} to ${part.name} dependencyResolution`) - if (this[dependencyList][part.name].indexOf(dep.name) === -1) - this[dependencyList][part.name].push(dep.name) -} - -/** - * Resolves/Adds a part's design configuration to the pattern config - * - * @private - * @param {Part} part - The part of which to resolve the config - * @param {onject} config - The global config - * @param {Store} store - The store, used for logging - * @return {object} config - The mutated global config - */ -Pattern.prototype.__addPartConfig = function (part) { - if (this.__resolvedParts.includes(part.name)) return this - - // Add parts, using set to keep them unique in the array - this.designConfig.parts = [...new Set(this.designConfig.parts).add(part)] - - return this.__addPartOptions(part) - .__addPartMeasurements(part) - .__addPartOptionalMeasurements(part) - .__addPartPlugins(part) -} - -/** - * Resolves/Adds a part's configured measurements to the global config - * - * @private - * @param {Part} part - The part of which to resolve the config - * @param {array} list - The list of resolved measurements - * @return {Pattern} this - The Pattern instance - */ -Pattern.prototype.__addPartMeasurements = function (part, list = false) { - if (!this.config.measurements) this.config.measurements = [] - if (!list) list = this.config.measurements - if (part.measurements) { - for (const m of part.measurements) { - if (list.indexOf(m) === -1) { - list.push(m) - this.store.log.debug(`🟠 __${m}__ measurement is required in \`${part.name}\``) - } - } - } - - // Weed out duplicates - this.config.measurements = [...new Set(list)] - - return this -} - -/** - * Resolves/Adds a part's configured optional measurements to the global config - * - * @private - * @param {Part} part - The part of which to resolve the config - * @param {array} list - The list of resolved optional measurements - * @return {Pattern} this - The Pattern instance - */ -Pattern.prototype.__addPartOptionalMeasurements = function (part, list = false) { - if (!this.config.optionalMeasurements) this.config.optionalMeasurements = [] - if (!list) list = this.config.optionalMeasurements - if (part.optionalMeasurements) { - for (const m of part.optionalMeasurements) { - // Don't add it's a required measurement for another part - if (this.config.measurements.indexOf(m) === -1) { - if (list.indexOf(m) === -1) { - list.push(m) - this.store.log.debug(`🟡 __${m}__ measurement is optional in \`${part.name}\``) - } - } - } - } - - // Weed out duplicates - if (list.length > 0) this.config.optionalMeasurements = [...new Set(list)] - - return this -} - -/** - * Resolves/Adds a part's configured options to the global config - * - * @private - * @param {Part} part - The part of which to resolve the config - * @return {Pattern} this - The Pattern instance - */ -Pattern.prototype.__addPartOptions = function (part) { - if (!this.config.options) this.config.options = {} - if (part.options) { - const partDistance = this.__mutated.partDistance?.[part.name] || 0 - for (const optionName in part.options) { - const optionDistance = this.__mutated.optionDistance[optionName] - if (!optionDistance) { - this.__mutated.optionDistance[optionName] = partDistance - // Keep design parts immutable in the pattern or risk subtle bugs - this.config.options[optionName] = Object.freeze(part.options[optionName]) - this.store.log.debug(`🔵 __${optionName}__ option loaded from part \`${part.name}\``) - } else { - if (DISTANCE_DEBUG) - this.store.log.debug( - `optionDistance for __${optionName}__ is __${optionDistance}__ and partDistance for \`${part.name}\` is __${partDistance}__` - ) - if (optionDistance > partDistance) { - this.config.options[optionName] = part.options[optionName] - this.__mutated.optionDistance[optionName] = partDistance - this.store.log.debug(`🟣 __${optionName}__ option overwritten by \`${part.name}\``) - } - } - } - } - - return this -} - -function getPluginName(plugin) { - if (Array.isArray(plugin)) { - if (plugin[0].name) return plugin[0].name - if (plugin[0].plugin.name) return plugin[0].plugin.name - } else { - if (plugin.name) return plugin.name - if (plugin.plugin?.name) return plugin.plugin.name - } - - return false -} - -/** - * Resolves/Adds a part's configured plugins to the global config - * - * @private - * @param {Part} part - The part of which to resolve the config - * @return {Pattern} this - The Pattern instance - */ -Pattern.prototype.__addPartPlugins = function (part) { - if (!part.plugins) return this - if (!this.config.plugins) this.config.plugins = {} - const plugins = { ...this.config.plugins } - // Side-step immutability of the part object to ensure plugins is an array - let partPlugins = part.plugins - if (!Array.isArray(partPlugins)) partPlugins = [partPlugins] - // Go through list of part plugins - for (let plugin of partPlugins) { - const name = getPluginName(plugin) - this.store.log.debug( - plugin.plugin - ? `🔌 Resolved __${name}__ conditional plugin in \`${part.name}\`` - : `🔌 Resolved __${name}__ plugin in \`${part.name}\`` - ) - // Handle [plugin, data] scenario - if (Array.isArray(plugin)) { - const pluginObj = { ...plugin[0], data: plugin[1] } - plugin = pluginObj - } - if (!plugins[name]) { - // New plugin, so we load it - plugins[name] = plugin - this.store.log.info( - plugin.condition - ? `New plugin conditionally added: \`${name}\`` - : `New plugin added: \`${name}\`` - ) - } else { - // Existing plugin, takes some more work - if (plugin.plugin && plugin.condition) { - // Multiple instances of the same plugin with different conditions - // will all be added, so we need to change the name. - if (plugins[name]?.condition) { - plugins[name + '_'] = plugin - this.store.log.info( - `Plugin \`${name}\` was conditionally added again. Renaming to ${name}_.` - ) - } else - this.store.log.info( - `Plugin \`${name}\` was requested conditionally, but is already added explicitly. Not loading.` - ) - } - // swap from a conditional if needed - else if (plugins[name].condition) { - plugins[name] = plugin - this.store.log.info(`Plugin \`${name}\` was explicitly added. Changing from conditional.`) - } - } - } - - this.config.plugins = { ...plugins } - - return this -} - /** * Creates a store for a set (of settings) * @@ -620,7 +403,7 @@ Pattern.prototype.__createPartWithContext = function (name, set) { const part = new Part() part.name = name part.set = set - part.stack = this.__designParts[name]?.stack || name + part.stack = this.config.parts[name]?.stack || name part.context = { parts: this.parts[set], config: this.config, @@ -700,14 +483,11 @@ Pattern.prototype.__init = function () { * This methods does that, and resolves the design config + user settings */ this.__resolveParts() // Resolves parts - .__resolveDependencies() // Resolves dependencies - .__resolveDraftOrder() // Resolves draft order + .__resolveConfig() // Gets the config from the resolver .__loadPlugins() // Loads plugins .__loadConfigData() // Makes config data available in store - .__filterOptionalMeasurements() // Removes required m's from optional list - .__loadOptionDefaults() // Merges default options with user provided ones - this.store.log.info(`Pattern initialized. Draft order is: ${this.__draftOrder.join(', ')}`) + this.store.log.info(`Pattern initialized. Draft order is: ${this.config.draftOrder.join(', ')}`) this.__runHooks('postInit') this.__initialized = true @@ -726,10 +506,10 @@ Pattern.prototype.__isPartHidden = function (partName) { if (Array.isArray(this.settings[this.activeSet || 0].only)) { if (this.settings[this.activeSet || 0].only.includes(partName)) return false } - if (this.__designParts?.[partName]?.hide) return true - if (this.__designParts?.[partName]?.hideAll) return true - if (this.__mutated.partHide?.[partName]) return true - if (this.__mutated.partHideAll?.[partName]) return true + if (this.config.parts?.[partName]?.hide) return true + if (this.config.parts?.[partName]?.hideAll) return true + if (this.config.partHide?.[partName]) return true + if (this.config.partHideAll?.[partName]) return true if (this.parts?.[this.activeSet]?.[partName]?.hidden) return true return false @@ -822,40 +602,6 @@ Pattern.prototype.__loadConfigData = function () { return this } -/** - * Merges defaults for options with user-provided options - * - * @private - * @return {Pattern} this - The Pattern instance - */ -Pattern.prototype.__loadOptionDefaults = function () { - if (!this.config.options) this.config.options = {} - if (Object.keys(this.config.options).length < 1) return this - for (const i in this.settings) { - for (const [name, option] of Object.entries(this.config.options)) { - // Don't overwrite user-provided settings.options - if (typeof this.settings[i].options[name] === 'undefined') { - if (typeof option === 'object') { - if (typeof option.pct !== 'undefined') this.settings[i].options[name] = option.pct / 100 - else if (typeof option.mm !== 'undefined') this.settings[i].options[name] = option.mm - else if (typeof option.deg !== 'undefined') this.settings[i].options[name] = option.deg - else if (typeof option.count !== 'undefined') - this.settings[i].options[name] = option.count - else if (typeof option.bool !== 'undefined') this.settings[i].options[name] = option.bool - else if (typeof option.dflt !== 'undefined') this.settings[i].options[name] = option.dflt - else { - let err = 'Unknown option type: ' + JSON.stringify(option) - this.store.log.error(err) - throw new Error(err) - } - } else this.settings[i].options[name] = option - } - } - } - - return this -} - /** * Loads a plugin * @@ -1049,8 +795,8 @@ Pattern.prototype.__needs = function (partName, set = 0) { // Walk the only parts, checking each one for a match in its dependencies for (const part of only) { if (part === partName) return true - if (this.__resolvedDependencies[part]) { - for (const dependency of this.__resolvedDependencies[part]) { + if (this.config.resolvedDependencies[part]) { + for (const dependency of this.config.resolvedDependencies[part]) { if (dependency === partName) return true } } @@ -1183,117 +929,11 @@ Pattern.prototype.__pack = function () { return this } -/** - * Resolves the draft order based on the configuation - * - * @private - * @param {object} graph - The object of resolved dependencies, used to call itself recursively - * @return {Pattern} this - The Pattern instance - */ -Pattern.prototype.__resolveDraftOrder = function (graph = this.__resolvedDependencies) { - const sorted = Object.keys(this.__designParts).sort( - (p1, p2) => this.__mutated.partDistance[p2] - this.__mutated.partDistance[p1] - ) - this.__draftOrder = sorted - this.config.draftOrder = sorted - +Pattern.prototype.__resolveConfig = function () { + this.config = this.__configResolver.asConfig() return this } -Pattern.prototype.__resolvePartDependencyChain = function (depChain, dependency, depType) { - const part = depChain[0] - - this.__designParts[dependency.name] = Object.freeze(dependency) - this.__addDependency('__dependencies', part, dependency) - - depChain.forEach((c) => this.__addDependency('__resolvedDependencies', c, dependency)) - - switch (depType) { - case 'from': - this.__setFromHide(part, part.name, dependency.name) - this.__inject[part.name] = dependency.name - break - case 'after': - this.__setAfterHide(part, part.name, dependency.name) - } -} - -Pattern.prototype.__resolveMutatedPartDistance = function (partName) { - const proposed_dependent_part_distance = this.__mutated.partDistance[partName] + 1 - let didChange = false - if (!this.__dependencies[partName]) return false - this.__dependencies[partName].forEach((dependency) => { - if ( - typeof this.__mutated.partDistance[dependency] === 'undefined' || - this.__mutated.partDistance[dependency] < proposed_dependent_part_distance - ) { - didChange = true - this.__mutated.partDistance[dependency] = proposed_dependent_part_distance - this.__resolveMutatedPartDistance(dependency) - } - if (DISTANCE_DEBUG) - this.store.log.debug( - `"${depType}:" partDistance for \`${dependency}\` is __${this.__mutated.partDistance[dependency]}__` - ) - }) - - return didChange -} - -const depTypes = ['from', 'after'] -Pattern.prototype.__resolvePartDependencies = function (depChain, distance) { - // Resolve part Dependencies. first from then after - const part = depChain[0] - this.__resolvedDependencies[part.name] = this.__resolvedDependencies[part.name] || [] - - depTypes.forEach((d) => { - if (part[d]) { - if (DISTANCE_DEBUG) this.store.log.debug(`Processing \`${part.name}\` "${d}:"`) - - const depsOfType = Array.isArray(part[d]) ? part[d] : [part[d]] - - depsOfType.forEach((dot) => { - let count = Object.keys(this.__designParts).length - // if any changes resulted from resolving this part mutation - this.__resolvePartDependencyChain(depChain, dot, d) - // if a new part was added, resolve the part - const newCount = Object.keys(this.__designParts).length - if (count < newCount) { - this.__resolvePart([dot, ...depChain], distance) - count = newCount - } - }) - } - }) - - this.__resolveMutatedPartDistance(part.name) -} - -Pattern.prototype.__resolvePart = function (depChain, distance = 0) { - const part = depChain[0] - if (distance === 0) { - this.__designParts[part.name] = Object.freeze(part) - } - distance++ - if (typeof this.__mutated.partDistance[part.name] === 'undefined') { - this.__mutated.partDistance[part.name] = distance - - if (DISTANCE_DEBUG) - this.store.log.debug( - `Base partDistance for \`${part.name}\` is __${this.__mutated.partDistance[part.name]}__` - ) - } - - // Hide when hideAll is set - if (part.hideAll) { - this.__mutated.partHide[part.name] = true - } - - this.__resolvePartDependencies(depChain, distance) - - // add the part's config - this.__addPartConfig(part) -} /** * Resolves parts and their dependencies * @@ -1302,35 +942,15 @@ Pattern.prototype.__resolvePart = function (depChain, distance = 0) { * @param {int} distance - Keeps track of how far the dependency is from the pattern * @return {Pattern} this - The Pattern instance */ -Pattern.prototype.__resolveParts = function (count = 0, distance = 0) { - for (const part of this.designConfig.parts) { - this.__resolvePart([part], distance) - } +Pattern.prototype.__resolveParts = function () { + this.designConfig.parts.forEach((p) => this.__configResolver.addPart(p)) // Print final part distances. - for (const part of this.designConfig.parts) { - let qualifier = DISTANCE_DEBUG ? 'final' : '' - this.store.log.debug( - `⚪️ \`${part.name}\` ${qualifier} options priority is __${ - this.__mutated.partDistance[part.name] - }__` - ) - } + this.__configResolver.logPartDistances() return this } -/** - * Resolves parts depdendencies into a flat array - * - * @private - * @return {Pattern} this - The Pattern instance - */ -Pattern.prototype.__resolveDependencies = function () { - this.config.resolvedDependencies = this.__resolvedDependencies - return this -} - /** * Runs subscriptions to a given lifecycle hook * @@ -1364,49 +984,6 @@ Pattern.prototype.__setBase = function () { } } -/** - * Sets visibility of a dependency based on its config - * - * @private - * @param {Part} part - The part of which this is a dependency - * @param {string} name - The name of the part - * @param {string} depName - The name of the dependency - * @param {int} set - The index of the set in the list of settings - * @return {Pattern} this - The Pattern instance - */ -Pattern.prototype.__setFromHide = function (part, name, depName) { - if ( - part.hideDependencies || - part.hideAll || - this.__mutated.partHide[name] || - this.__mutated.partHideAll[name] - ) { - this.__mutated.partHide[depName] = true - this.__mutated.partHideAll[depName] = true - } - - return this -} - -/** - * Sets visibility of an 'after' dependency based on its config - * - * @private - * @param {Part} part - The part of which this is a dependency - * @param {string} name - The name of the part - * @param {string} depName - The name of the dependency - * @param {int} set - The index of the set in the list of settings - * @return {Pattern} this - The Pattern instance - */ -Pattern.prototype.__setAfterHide = function (part, name, depName) { - if (this.__mutated.partHide[name] || this.__mutated.partHideAll[name]) { - this.__mutated.partHide[depName] = true - this.__mutated.partHideAll[depName] = true - } - - return this -} - /** * Returns the absolute value of a snapped percentage option * @@ -1493,22 +1070,3 @@ Pattern.prototype.__wants = function (partName, set = 0) { return true } - -////////////////////////////////////////////// -// STATIC PRIVATE FUNCTIONS // -////////////////////////////////////////////// - -/** - * Merges dependencies into a flat list - * - * @private - * @param {array} dep - New dependencies - * @param {array} current - Current dependencies - * @return {array} deps - Merged dependencies - */ -function mergeDependencies(dep = [], current = []) { - // Current dependencies - const list = [].concat(current, dep) - - return [...new Set(list)] -} diff --git a/packages/core/src/patternConfig.mjs b/packages/core/src/patternConfig.mjs new file mode 100644 index 00000000000..a28abb1b432 --- /dev/null +++ b/packages/core/src/patternConfig.mjs @@ -0,0 +1,419 @@ +import { __addNonEnumProp } from './utils.mjs' + +export function getPluginName(plugin) { + if (Array.isArray(plugin)) { + if (plugin[0].name) return plugin[0].name + if (plugin[0].plugin.name) return plugin[0].plugin.name + } else { + if (plugin.name) return plugin.name + if (plugin.plugin?.name) return plugin.plugin.name + } + + return false +} + +export function PatternConfig(pattern) { + this.pattern = pattern + this.store = pattern.store + __addNonEnumProp(this, 'plugins', { ...(pattern.designConfig.plugins || {}) }) + __addNonEnumProp(this, 'options', { ...(pattern.designConfig.options || {}) }) + __addNonEnumProp(this, 'measurements', [...(pattern.designConfig.measurements || [])]) + __addNonEnumProp(this, 'optionalMeasurements', [ + ...(pattern.designConfig.optionalMeasurements || []), + ]) + __addNonEnumProp(this, 'inject', {}) + __addNonEnumProp(this, 'directDependencies', {}) + __addNonEnumProp(this, 'resolvedDependencies', {}) + __addNonEnumProp(this, 'parts', {}) + __addNonEnumProp(this, '__resolvedParts', {}) + __addNonEnumProp(this, '__mutated', { + optionDistance: {}, + partDistance: {}, + partHide: {}, + partHideAll: {}, + }) +} + +const DISTANCE_DEBUG = false + +PatternConfig.prototype.validatePart = function (part) { + if (typeof part?.draft !== 'function') { + this.store.log.error(`Part must have a draft() method`) + return false + } + + if (!part.name) { + this.store.log.error(`Part must have a name`) + return false + } + + return true +} +PatternConfig.prototype.addPart = function (part) { + if (this.validatePart(part)) this.__resolvePart([part]) + + return this +} + +PatternConfig.prototype.logPartDistances = function () { + for (const partName in this.parts) { + let qualifier = DISTANCE_DEBUG ? 'final' : '' + this.store.log.debug( + `⚪️ \`${partName}\` ${qualifier} options priority is __${this.__mutated.partDistance[partName]}__` + ) + } +} + +PatternConfig.prototype.asConfig = function () { + return { + parts: this.parts, + plugins: this.plugins, + measurements: this.measurements, + options: this.options, + optionalMeasurements: this.optionalMeasurements, + resolvedDependencies: this.resolvedDependencies, + directDependencies: this.directDependencies, + inject: this.inject, + draftOrder: this.__resolveDraftOrder(), + partHide: this.__mutated.partHide, + partHideAll: this.__mutated.partHideAll, + } +} + +PatternConfig.prototype.__resolvePart = function (depChain, distance = 0) { + const part = depChain[0] + if (distance === 0) { + this.parts[part.name] = Object.freeze(part) + } + distance++ + if (typeof this.__mutated.partDistance[part.name] === 'undefined') { + this.__mutated.partDistance[part.name] = distance + + if (DISTANCE_DEBUG) + this.store.log.debug( + `Base partDistance for \`${part.name}\` is __${this.__mutated.partDistance[part.name]}__` + ) + } + + // Hide when hideAll is set + if (part.hideAll) { + this.__mutated.partHide[part.name] = true + } + + this.__resolvePartDependencies(depChain, distance) + + // add the part's config + this.__addPartConfig(part) +} + +/** + * Resolves/Adds a part's design configuration to the pattern config + * + * @private + * @param {Part} part - The part of which to resolve the config + * @param {onject} config - The global config + * @param {Store} store - The store, used for logging + * @return {object} config - The mutated global config + */ +PatternConfig.prototype.__addPartConfig = function (part) { + if (this.__resolvedParts[part.name]) return this + + // Add parts, using set to keep them unique in the array + // this.designConfig.parts = [...new Set(this.designConfig.parts).add(part)] + + return this.__addPartOptions(part) + .__addPartMeasurements(part, true) + .__addPartMeasurements(part, false) + .__addPartPlugins(part) +} + +/** + * Resolves/Adds a part's configured options to the global config + * + * @private + * @param {Part} part - The part of which to resolve the config + * @return {Pattern} this - The Pattern instance + */ +PatternConfig.prototype.__addPartOptions = function (part) { + if (!part.options) return this + + const partDistance = this.__mutated.partDistance?.[part.name] || 0 + for (const optionName in part.options) { + const option = part.options[optionName] + const optionDistance = this.__mutated.optionDistance[optionName] + if (optionDistance && DISTANCE_DEBUG) + this.store.log.debug( + `optionDistance for __${optionName}__ is __${optionDistance}__ and partDistance for \`${part.name}\` is __${partDistance}__` + ) + if (!optionDistance || optionDistance > partDistance) { + this.__mutated.optionDistance[optionName] = partDistance + // Keep design parts immutable in the pattern or risk subtle bugs + this.options[optionName] = Object.freeze(option) + this.store.log.debug( + optionDistance + ? `🟣 __${optionName}__ option overwritten by \`${part.name}\`` + : `🔵 __${optionName}__ option loaded from part \`${part.name}\`` + ) + this.__loadOptionDefault(optionName, option) + } + } + + return this +} + +/** + * Resolves/Adds a part's configured measurements to the global config + * + * @private + * @param {Part} part - The part of which to resolve the config + * @param {array} list - The list of resolved measurements + * @return {Pattern} this - The Pattern instance + */ +PatternConfig.prototype.__addPartMeasurements = function (part, optional = false) { + const listType = optional ? 'optionalMeasurements' : 'measurements' + if (part[listType]) { + part[listType].forEach((m) => { + const isInReqList = this.measurements.indexOf(m) !== -1 + const optInd = this.optionalMeasurements.indexOf(m) + const isInOptList = optInd !== -1 + + if (isInReqList) return + if (optional && !isInOptList) this.optionalMeasurements.push(m) + if (!optional) { + this.measurements.push(m) + + if (isInOptList) this.optionalMeasurements.splice(optInd, 1) + } + + this.store.log.debug( + `🟠 __${m}__ measurement is ${optional ? 'optional' : 'required'} in \`${part.name}\`` + ) + }) + } + + return this +} + +/** + * Resolves/Adds a part's configured plugins to the global config + * + * @private + * @param {Part} part - The part of which to resolve the config + * @return {Pattern} this - The Pattern instance + */ +PatternConfig.prototype.__addPartPlugins = function (part) { + if (!part.plugins) return this + + const plugins = this.plugins + // Side-step immutability of the part object to ensure plugins is an array + let partPlugins = part.plugins + if (!Array.isArray(partPlugins)) partPlugins = [partPlugins] + // Go through list of part plugins + for (let plugin of partPlugins) { + const name = getPluginName(plugin) + this.store.log.debug( + plugin.plugin + ? `🔌 Resolved __${name}__ conditional plugin in \`${part.name}\`` + : `🔌 Resolved __${name}__ plugin in \`${part.name}\`` + ) + // Handle [plugin, data] scenario + if (Array.isArray(plugin)) { + const pluginObj = { ...plugin[0], data: plugin[1] } + plugin = pluginObj + } + if (!plugins[name]) { + // New plugin, so we load it + plugins[name] = plugin + this.store.log.info( + plugin.condition + ? `New plugin conditionally added: \`${name}\`` + : `New plugin added: \`${name}\`` + ) + } else { + // Existing plugin, takes some more work + if (plugin.plugin && plugin.condition) { + // Multiple instances of the same plugin with different conditions + // will all be added, so we need to change the name. + if (plugins[name]?.condition) { + plugins[name + '_'] = plugin + this.store.log.info( + `Plugin \`${name}\` was conditionally added again. Renaming to ${name}_.` + ) + } else + this.store.log.info( + `Plugin \`${name}\` was requested conditionally, but is already added explicitly. Not loading.` + ) + } + // swap from a conditional if needed + else if (plugins[name].condition) { + plugins[name] = plugin + this.store.log.info(`Plugin \`${name}\` was explicitly added. Changing from conditional.`) + } + } + } + + return this +} + +PatternConfig.prototype.__loadOptionDefault = function (optionName, option) { + this.pattern.settings.forEach((set) => { + if (typeof set.options[optionName] !== 'undefined') return + if (typeof option === 'object') { + if (typeof option.pct !== 'undefined') set.options[optionName] = option.pct / 100 + else if (typeof option.mm !== 'undefined') set.options[optionName] = option.mm + else if (typeof option.deg !== 'undefined') set.options[optionName] = option.deg + else if (typeof option.count !== 'undefined') set.options[optionName] = option.count + else if (typeof option.bool !== 'undefined') set.options[optionName] = option.bool + else if (typeof option.dflt !== 'undefined') set.options[optionName] = option.dflt + else { + let err = 'Unknown option type: ' + JSON.stringify(option) + this.store.log.error(err) + throw new Error(err) + } + } else set.options[optionName] = option + }) +} + +PatternConfig.prototype.__resolvePartDependencyChain = function (depChain, dependency, depType) { + const part = depChain[0] + + this.parts[dependency.name] = Object.freeze(dependency) + this.__addDependency('directDependencies', part, dependency) + + depChain.forEach((c) => this.__addDependency('resolvedDependencies', c, dependency)) + + switch (depType) { + case 'from': + this.__setFromHide(part, part.name, dependency.name) + this.inject[part.name] = dependency.name + break + case 'after': + this.__setAfterHide(part, part.name, dependency.name) + } +} + +PatternConfig.prototype.__resolveMutatedPartDistance = function (partName) { + const proposed_dependent_part_distance = this.__mutated.partDistance[partName] + 1 + let didChange = false + if (!this.directDependencies[partName]) return false + this.directDependencies[partName].forEach((dependency) => { + if ( + typeof this.__mutated.partDistance[dependency] === 'undefined' || + this.__mutated.partDistance[dependency] < proposed_dependent_part_distance + ) { + didChange = true + this.__mutated.partDistance[dependency] = proposed_dependent_part_distance + this.__resolveMutatedPartDistance(dependency) + } + if (DISTANCE_DEBUG) + this.store.log.debug( + `partDistance for \`${dependency}\` is __${this.__mutated.partDistance[dependency]}__` + ) + }) + + return didChange +} + +const depTypes = ['from', 'after'] +PatternConfig.prototype.__resolvePartDependencies = function (depChain, distance) { + // Resolve part Dependencies. first from then after + const part = depChain[0] + this.resolvedDependencies[part.name] = this.resolvedDependencies[part.name] || [] + + depTypes.forEach((d) => { + if (part[d]) { + if (DISTANCE_DEBUG) this.store.log.debug(`Processing \`${part.name}\` "${d}:"`) + + const depsOfType = Array.isArray(part[d]) ? part[d] : [part[d]] + + depsOfType.forEach((dot) => { + let count = Object.keys(this.parts).length + // if any changes resulted from resolving this part mutation + this.__resolvePartDependencyChain(depChain, dot, d) + // if a new part was added, resolve the part + const newCount = Object.keys(this.parts).length + if (count < newCount) { + this.__resolvePart([dot, ...depChain], distance) + count = newCount + } + }) + } + }) + + this.__resolveMutatedPartDistance(part.name) +} + +/** + * Adds a part as a simple dependency + * + * @private + * @param {string} name - The name of the dependency + * @param {object} part - The part configuration + * @param {object} dep - The dependency configuration + * @return {object} this - The Pattern instance + */ +PatternConfig.prototype.__addDependency = function (dependencyList, part, dep) { + this[dependencyList][part.name] = this[dependencyList][part.name] || [] + if (dependencyList == 'resolvedDependencies' && DISTANCE_DEBUG) + this.store.log.debug(`add ${dep.name} to ${part.name} dependencyResolution`) + if (this[dependencyList][part.name].indexOf(dep.name) === -1) + this[dependencyList][part.name].push(dep.name) +} + +/** + * Resolves the draft order based on the configuation + * + * @private + * @param {object} graph - The object of resolved dependencies, used to call itself recursively + * @return {Pattern} this - The Pattern instance + */ +PatternConfig.prototype.__resolveDraftOrder = function () { + this.__draftOrder = Object.keys(this.parts).sort( + (p1, p2) => this.__mutated.partDistance[p2] - this.__mutated.partDistance[p1] + ) + + return this.__draftOrder +} + +/** + * Sets visibility of a dependency based on its config + * + * @private + * @param {Part} part - The part of which this is a dependency + * @param {string} name - The name of the part + * @param {string} depName - The name of the dependency + * @param {int} set - The index of the set in the list of settings + * @return {Pattern} this - The Pattern instance + */ +PatternConfig.prototype.__setFromHide = function (part, name, depName) { + if ( + part.hideDependencies || + part.hideAll || + this.__mutated.partHide[name] || + this.__mutated.partHideAll[name] + ) { + this.__mutated.partHide[depName] = true + this.__mutated.partHideAll[depName] = true + } + + return this +} + +/** + * Sets visibility of an 'after' dependency based on its config + * + * @private + * @param {Part} part - The part of which this is a dependency + * @param {string} name - The name of the part + * @param {string} depName - The name of the dependency + * @param {int} set - The index of the set in the list of settings + * @return {Pattern} this - The Pattern instance + */ +PatternConfig.prototype.__setAfterHide = function (part, name, depName) { + if (this.__mutated.partHide[name] || this.__mutated.partHideAll[name]) { + this.__mutated.partHide[depName] = true + this.__mutated.partHideAll[depName] = true + } + + return this +} diff --git a/packages/core/tests/pattern-init.test.mjs b/packages/core/tests/pattern-init.test.mjs index 8f5c76621d7..036d91e89bd 100644 --- a/packages/core/tests/pattern-init.test.mjs +++ b/packages/core/tests/pattern-init.test.mjs @@ -32,12 +32,12 @@ describe('Pattern', () => { expect(typeof pattern.Snippet).to.equal('function') expect(typeof pattern.Attributes).to.equal('function') expect(typeof pattern.macros).to.equal('object') - expect(typeof pattern.__designParts).to.equal('object') - expect(typeof pattern.__inject).to.equal('object') - expect(typeof pattern.__dependencies).to.equal('object') - expect(typeof pattern.__resolvedDependencies).to.equal('object') - expect(typeof pattern.__hide).to.equal('object') - expect(Array.isArray(pattern.__draftOrder)).to.equal(true) + // expect(typeof pattern.__designParts).to.equal('object') + // expect(typeof pattern.config.inject).to.equal('object') + // expect(typeof pattern.config.directDependencies).to.equal('object') + // expect(typeof pattern.__resolvedDependencies).to.equal('object') + // expect(typeof pattern.__hide).to.equal('object') + // expect(Array.isArray(pattern.__draftOrder)).to.equal(true) expect(pattern.width).to.equal(0) expect(pattern.height).to.equal(0) expect(pattern.is).to.equal('') @@ -145,7 +145,7 @@ describe('Pattern', () => { }) it('Pattern.__init() should resolve parts', () => { - expect(pattern.designConfig.parts.length).to.equal(3) + expect(Object.keys(pattern.config.parts)).to.have.lengthOf(3) }) it('Pattern.__init() should resolve plugins', () => { @@ -324,14 +324,14 @@ describe('Pattern', () => { expect(pattern.config.options.optionR.list[1]).to.equal('green') expect(pattern.config.options.optionR.list[2]).to.equal('blue') // Dependencies - expect(pattern.__dependencies.partB).to.include('partA') - expect(pattern.__dependencies.partC).to.include('partB') - expect(pattern.__dependencies.partR).to.include('partC') - expect(pattern.__dependencies.partR).to.include('partA') + expect(pattern.config.directDependencies.partB).to.include('partA') + expect(pattern.config.directDependencies.partC).to.include('partB') + expect(pattern.config.directDependencies.partR).to.include('partC') + expect(pattern.config.directDependencies.partR).to.include('partA') // Inject - expect(pattern.__inject.partB).to.equal('partA') - expect(pattern.__inject.partC).to.equal('partB') - expect(pattern.__inject.partR).to.equal('partA') + expect(pattern.config.inject.partB).to.equal('partA') + expect(pattern.config.inject.partC).to.equal('partB') + expect(pattern.config.inject.partR).to.equal('partA') // Draft order expect(pattern.config.draftOrder[0]).to.equal('partA') expect(pattern.config.draftOrder[1]).to.equal('partB') @@ -472,12 +472,12 @@ describe('Pattern', () => { expect(pattern.config.options.optionD.list[1]).to.equal('green') expect(pattern.config.options.optionD.list[2]).to.equal('blue') // Dependencies - expect(pattern.__dependencies.partB[0]).to.equal('partA') - expect(pattern.__dependencies.partC[0]).to.equal('partB') - expect(pattern.__dependencies.partD[0]).to.equal('partC') + expect(pattern.config.directDependencies.partB[0]).to.equal('partA') + expect(pattern.config.directDependencies.partC[0]).to.equal('partB') + expect(pattern.config.directDependencies.partD[0]).to.equal('partC') // Inject - expect(pattern.__inject.partB).to.equal('partA') - expect(pattern.__inject.partC).to.equal('partB') + expect(pattern.config.inject.partB).to.equal('partA') + expect(pattern.config.inject.partC).to.equal('partB') // Draft order expect(pattern.config.draftOrder[0]).to.equal('partA') expect(pattern.config.draftOrder[1]).to.equal('partB') diff --git a/packages/core/tests/pattern-runtime-parts.test.mjs b/packages/core/tests/pattern-runtime-parts.test.mjs index a3a640e6721..dbe71799dbb 100644 --- a/packages/core/tests/pattern-runtime-parts.test.mjs +++ b/packages/core/tests/pattern-runtime-parts.test.mjs @@ -12,31 +12,31 @@ describe('Pattern', () => { const part2 = { name: 'test2', - from: part1, + after: part1, draft: ({ part }) => part, } const part3 = { name: 'test3', - after: part2, + from: part2, draft: ({ part }) => part, } describe('with resolveImmediately: true', () => { - it('Should add the part to the internal part object', () => { + it('Should add the part to parts object', () => { const design = new Design({ parts: [part1] }) const pattern = new design() pattern.__init() pattern.addPart(part2, true) - expect(pattern.__designParts.test2).to.equal(part2) + expect(pattern.config.parts.test2).to.equal(part2) }) it('Should resolve injected dependencies for the new part', () => { const design = new Design({ parts: [part1] }) const pattern = new design() pattern.__init() - pattern.addPart(part2, true) - expect(pattern.__inject.test2).to.equal('test') + pattern.addPart(part3, true) + expect(pattern.config.inject.test3).to.equal('test2') }) it('Should resolve all dependencies for the new part', () => { @@ -45,7 +45,7 @@ describe('Pattern', () => { pattern.__init() pattern.addPart(part3, true) expect(pattern.config.resolvedDependencies.test3).to.have.members(['test', 'test2']) - expect(pattern.__designParts.test2).to.equal(part2) + expect(pattern.config.parts.test2).to.equal(part2) }) it('Should add a the measurements for the new part', () => { @@ -79,7 +79,7 @@ describe('Pattern', () => { expect(pattern.config.plugins.testPlugin).to.equal(plugin) }) - it('Should add the options for the new part', () => { + it('Should resolve the options for the new part', () => { const design = new Design({ parts: [part1] }) const pattern = new design() pattern.__init() @@ -96,6 +96,61 @@ describe('Pattern', () => { pattern.addPart(part2, true) expect(pattern.config.options.opt1).to.equal(opt1) }) + + it('Should resolve the dependency options for the new part', () => { + const design = new Design({ parts: [part1] }) + const pattern = new design() + pattern.__init() + + const opt1 = { pct: 10, min: 0, max: 50 } + const part2 = { + name: 'test2', + options: { + opt1, + }, + draft: ({ part }) => part, + } + + const part3 = { + name: 'test3', + from: part2, + draft: ({ part }) => part, + } + + pattern.addPart(part3, true) + expect(pattern.config.options.opt1).to.equal(opt1) + }) + + it('Should resolve the overwritten options for the new part', () => { + const design = new Design({ parts: [part1] }) + const pattern = new design() + pattern.__init() + + const opt1 = { pct: 10, min: 0, max: 50 } + const part2 = { + name: 'test2', + options: { + opt1: { pct: 15, min: 10, max: 55 }, + }, + draft: ({ part }) => part, + } + + const part3 = { + name: 'test3', + from: part2, + options: { + opt1, + }, + draft: ({ part }) => part, + } + + pattern.addPart(part3, true) + expect(pattern.config.options.opt1).to.equal(opt1) + }) + }) + + describe('with resolveImmediately: false', () => { + it('does not create duplications in the configuration') }) }) }) diff --git a/packages/core/tests/snap.test.mjs b/packages/core/tests/snap.test.mjs index fa64a1f9f6b..4c51de52472 100644 --- a/packages/core/tests/snap.test.mjs +++ b/packages/core/tests/snap.test.mjs @@ -42,6 +42,7 @@ describe('Snapped options', () => { snap: [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144], }, }, + draft: ({ part }) => part, } const design = new Design({ parts: [part] }) const patternA = new design({ options: { test: 0.13 }, measurements }).draft() @@ -67,6 +68,7 @@ describe('Snapped options', () => { }, }, }, + draft: ({ part }) => part, } const design = new Design({ parts: [part] }) const patternA = new design({ options: { test: 0.13 }, measurements, units: 'metric' }).draft() @@ -94,6 +96,7 @@ describe('Snapped options', () => { }, }, }, + draft: ({ part }) => part, } const design = new Design({ parts: [part] }) const patternA = new design({ From a322e2108bbad07835363344b4116efe8beb2488 Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Wed, 22 Feb 2023 16:09:43 +0200 Subject: [PATCH 27/36] document and refactor --- packages/core/src/pattern.mjs | 71 ++++-- packages/core/src/patternConfig.mjs | 353 +++++++++++++++++----------- packages/core/src/store.mjs | 1 + 3 files changed, 260 insertions(+), 165 deletions(-) diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index d593c360304..6c4ce537588 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -26,6 +26,12 @@ import cloneDeep from 'lodash.clonedeep' * @return {object} this - The Pattern instance */ export function Pattern(designConfig = {}) { + // Enumerable properties + this.designConfig = cloneDeep(designConfig) // The design configuration (unresolved) + this.config = {} // Will hold the resolved pattern after calling __init() + this.store = new Store() // Pattern-wide store + this.setStores = [] // Per-set stores + // Non-enumerable properties __addNonEnumProp(this, 'plugins', {}) __addNonEnumProp(this, 'width', 0) @@ -42,13 +48,6 @@ export function Pattern(designConfig = {}) { __addNonEnumProp(this, 'config.parts', {}) __addNonEnumProp(this, 'config.resolvedDependencies', {}) __addNonEnumProp(this, '__storeMethods', new Set()) - __addNonEnumProp(this, '__hide', {}) - - // Enumerable properties - this.designConfig = cloneDeep(designConfig) // The design configuration (unresolved) - this.config = {} // Will hold the resolved pattern after calling __init() - this.store = new Store() // Pattern-wide store - this.setStores = [] // Per-set stores __addNonEnumProp(this, '__configResolver', new PatternConfig(this)) // handles config resolution during __init() as well as runtime part adding return this @@ -59,9 +58,12 @@ export function Pattern(designConfig = {}) { ////////////////////////////////////////////// /** - * FIXME: Allows adding parts to the config at runtime + * Allows adding parts to the config at runtime * * @param {object} part - The part to add + * @param {boolean} resolveImmediately - Should the part be resolved now, or wait until the next call to {@link __init()}? + * It is useful to resolve immediately if one part is being added at runtime + * It might be useful to not resolve immediately if a number of parts will be added over multiple calls * @return {object} this - The Pattern instance */ Pattern.prototype.addPart = function (part, resolveImmediately = false) { @@ -443,24 +445,6 @@ Pattern.prototype.__createStackWithContext = function (name) { return stack } -/** - * Filter optional measurements out id they are also required measurments - * - * @private - * @return {Pattern} this - The Pattern instance - */ -Pattern.prototype.__filterOptionalMeasurements = function () { - if (!this.config.optionalMeasurements) { - this.config.optionalMeasurements = [] - return this - } - this.config.optionalMeasurements = this.config.optionalMeasurements.filter( - (m) => this.config.measurements.indexOf(m) === -1 - ) - - return this -} - /** * Initializes the pattern coniguration and settings * @@ -484,6 +468,7 @@ Pattern.prototype.__init = function () { */ this.__resolveParts() // Resolves parts .__resolveConfig() // Gets the config from the resolver + .__loadOptionDefaults() // Merges default options with user provided ones .__loadPlugins() // Loads plugins .__loadConfigData() // Makes config data available in store @@ -602,6 +587,40 @@ Pattern.prototype.__loadConfigData = function () { return this } +/** + * Merges defaults for options with user-provided options + * + * @private + * @return {Pattern} this - The Pattern instance + */ +Pattern.prototype.__loadOptionDefaults = function () { + if (!this.config.options) this.config.options = {} + if (Object.keys(this.config.options).length < 1) return this + for (const i in this.settings) { + for (const [name, option] of Object.entries(this.config.options)) { + // Don't overwrite user-provided settings.options + if (typeof this.settings[i].options[name] === 'undefined') { + if (typeof option === 'object') { + if (typeof option.pct !== 'undefined') this.settings[i].options[name] = option.pct / 100 + else if (typeof option.mm !== 'undefined') this.settings[i].options[name] = option.mm + else if (typeof option.deg !== 'undefined') this.settings[i].options[name] = option.deg + else if (typeof option.count !== 'undefined') + this.settings[i].options[name] = option.count + else if (typeof option.bool !== 'undefined') this.settings[i].options[name] = option.bool + else if (typeof option.dflt !== 'undefined') this.settings[i].options[name] = option.dflt + else { + let err = 'Unknown option type: ' + JSON.stringify(option) + this.store.log.error(err) + throw new Error(err) + } + } else this.settings[i].options[name] = option + } + } + } + + return this +} + /** * Loads a plugin * diff --git a/packages/core/src/patternConfig.mjs b/packages/core/src/patternConfig.mjs index a28abb1b432..79fa1083cb4 100644 --- a/packages/core/src/patternConfig.mjs +++ b/packages/core/src/patternConfig.mjs @@ -1,41 +1,72 @@ import { __addNonEnumProp } from './utils.mjs' +/** + * Get the name of the given plugin config + * + * @param {(Object|Object[])} plugin the plugin to get the name of + * @return {(string|false)} the name, or false if there isn't one + */ export function getPluginName(plugin) { - if (Array.isArray(plugin)) { - if (plugin[0].name) return plugin[0].name - if (plugin[0].plugin.name) return plugin[0].plugin.name - } else { - if (plugin.name) return plugin.name - if (plugin.plugin?.name) return plugin.plugin.name - } + const toCheck = Array.isArray(plugin) ? plugin[0] : plugin + return toCheck.name || toCheck.plugin?.name || false return false } +///////////////// +// CONSTRUCTOR // +///////////////// +/** + * A class for handling config resolution for a Pattern + * @class + * @param {Pattern} pattern the pattern whose config is being handled + */ export function PatternConfig(pattern) { - this.pattern = pattern + /** @type {Store} the pattern's store, for logging */ this.store = pattern.store - __addNonEnumProp(this, 'plugins', { ...(pattern.designConfig.plugins || {}) }) - __addNonEnumProp(this, 'options', { ...(pattern.designConfig.options || {}) }) - __addNonEnumProp(this, 'measurements', [...(pattern.designConfig.measurements || [])]) - __addNonEnumProp(this, 'optionalMeasurements', [ - ...(pattern.designConfig.optionalMeasurements || []), - ]) - __addNonEnumProp(this, 'inject', {}) - __addNonEnumProp(this, 'directDependencies', {}) - __addNonEnumProp(this, 'resolvedDependencies', {}) - __addNonEnumProp(this, 'parts', {}) + + /** @type {Object} resolved plugins keyed by name */ + this.plugins = { ...(pattern.designConfig.plugins || {}) } + /** @type {Object} resolved options keyed by name */ + this.options = { ...(pattern.designConfig.options || {}) } + /** @type {string[]} required measurements */ + this.measurements = [...(pattern.designConfig.measurements || [])] + /** @type {string[]} optional measurements */ + this.optionalMeasurements = [...(pattern.designConfig.optionalMeasurements || [])] + /** @type {Object} the names of the parts that will be injected */ + this.inject = {} + /** @type {Object} arrays of parts that are direct dependencies of the key */ + this.directDependencies = {} + /** @type {Object} arrays of all dependencies of the key */ + this.resolvedDependencies = {} + /** @type {Object} parts to include in the pattern */ + this.parts = {} + /** @type {Object} which parts are hidden */ + this.partHide = {} + /** @type {Object} which parts hide all their dependencies */ + this.partHideAll = {} + + /** to track which parts have already been resolved */ __addNonEnumProp(this, '__resolvedParts', {}) + /** @type {Object} to track when to overwrite options */ __addNonEnumProp(this, '__mutated', { optionDistance: {}, partDistance: {}, - partHide: {}, - partHideAll: {}, }) } +/** @type {Boolean} change me to true to get full debugging of the resolution process */ const DISTANCE_DEBUG = false +//////////////////// +// PUBLIC METHODs // +//////////////////// + +/** + * Validate that a part meets the requirements to be added to the pattern + * @param {Object} part a part configuration + * @return {boolean} whether the part is valid + */ PatternConfig.prototype.validatePart = function (part) { if (typeof part?.draft !== 'function') { this.store.log.error(`Part must have a draft() method`) @@ -49,12 +80,18 @@ PatternConfig.prototype.validatePart = function (part) { return true } + +/** + * Chainable method to add a part to the configuration + * @param {Object} part + */ PatternConfig.prototype.addPart = function (part) { - if (this.validatePart(part)) this.__resolvePart([part]) + if (this.validatePart(part)) this.__addPart([part]) return this } +/** Log the final report on part inheritance order */ PatternConfig.prototype.logPartDistances = function () { for (const partName in this.parts) { let qualifier = DISTANCE_DEBUG ? 'final' : '' @@ -64,6 +101,10 @@ PatternConfig.prototype.logPartDistances = function () { } } +/** + * Return a configuration in the structure expected by the pattern + * @return {Object} contains parts, plugins, measurements, options, optionalMeasurements, resolvedDependencies, directDependencies, inject, draftOrder, partHide, and partHideAll + */ PatternConfig.prototype.asConfig = function () { return { parts: this.parts, @@ -75,17 +116,30 @@ PatternConfig.prototype.asConfig = function () { directDependencies: this.directDependencies, inject: this.inject, draftOrder: this.__resolveDraftOrder(), - partHide: this.__mutated.partHide, - partHideAll: this.__mutated.partHideAll, + partHide: this.partHide, + partHideAll: this.partHideAll, } } -PatternConfig.prototype.__resolvePart = function (depChain, distance = 0) { +///////////////////// +// PRIVATE METHODS // +///////////////////// + +/** + * Add a part's configuration + * Uses recursion to also add that part's dependencies + * @private + * @param {Object[]} depChain an array starting with the current part to add and containing its dependents/descendents in order + */ +PatternConfig.prototype.__addPart = function (depChain) { + // the current part is the head of the chain const part = depChain[0] - if (distance === 0) { + // the longer the chain, the deeper the part is down it + const distance = depChain.length + if (!this.parts[part.name]) { this.parts[part.name] = Object.freeze(part) } - distance++ + // if it hasn't been registered with a distance, do that now if (typeof this.__mutated.partDistance[part.name] === 'undefined') { this.__mutated.partDistance[part.name] = distance @@ -97,10 +151,11 @@ PatternConfig.prototype.__resolvePart = function (depChain, distance = 0) { // Hide when hideAll is set if (part.hideAll) { - this.__mutated.partHide[part.name] = true + this.partHide[part.name] = true } - this.__resolvePartDependencies(depChain, distance) + // resolve its dependencies + this.__resolvePartDependencies(depChain) // add the part's config this.__addPartConfig(part) @@ -111,20 +166,16 @@ PatternConfig.prototype.__resolvePart = function (depChain, distance = 0) { * * @private * @param {Part} part - The part of which to resolve the config - * @param {onject} config - The global config - * @param {Store} store - The store, used for logging - * @return {object} config - The mutated global config + * @return this */ PatternConfig.prototype.__addPartConfig = function (part) { + // don't resolve a part that's already been resolved if (this.__resolvedParts[part.name]) return this - // Add parts, using set to keep them unique in the array - // this.designConfig.parts = [...new Set(this.designConfig.parts).add(part)] - - return this.__addPartOptions(part) - .__addPartMeasurements(part, true) - .__addPartMeasurements(part, false) - .__addPartPlugins(part) + return this.__addPartOptions(part) // add options + .__addPartMeasurements(part, false) // add required measurements + .__addPartMeasurements(part, true) // add optional measurements + .__addPartPlugins(part) // add plugins } /** @@ -132,29 +183,38 @@ PatternConfig.prototype.__addPartConfig = function (part) { * * @private * @param {Part} part - The part of which to resolve the config - * @return {Pattern} this - The Pattern instance + * @return {PatternConfig} this - The PatternConfig instance */ PatternConfig.prototype.__addPartOptions = function (part) { + // skip empty options if (!part.options) return this + // get the part's option priority const partDistance = this.__mutated.partDistance?.[part.name] || 0 + + // loop through options for (const optionName in part.options) { const option = part.options[optionName] + // get the priority of this option's current registration const optionDistance = this.__mutated.optionDistance[optionName] + // debug the comparison if (optionDistance && DISTANCE_DEBUG) this.store.log.debug( `optionDistance for __${optionName}__ is __${optionDistance}__ and partDistance for \`${part.name}\` is __${partDistance}__` ) + + // if it's never been registered, or it's registered at a further distance if (!optionDistance || optionDistance > partDistance) { - this.__mutated.optionDistance[optionName] = partDistance - // Keep design parts immutable in the pattern or risk subtle bugs + // Keep options immutable in the pattern or risk subtle bugs this.options[optionName] = Object.freeze(option) + // register the new distance + this.__mutated.optionDistance[optionName] = partDistance + // debug appropriately this.store.log.debug( optionDistance ? `🟣 __${optionName}__ option overwritten by \`${part.name}\`` : `🔵 __${optionName}__ option loaded from part \`${part.name}\`` ) - this.__loadOptionDefault(optionName, option) } } @@ -166,22 +226,32 @@ PatternConfig.prototype.__addPartOptions = function (part) { * * @private * @param {Part} part - The part of which to resolve the config - * @param {array} list - The list of resolved measurements - * @return {Pattern} this - The Pattern instance + * @param {boolean} optional - are these measurements optional? + * @return {PatternConfig} this - The PatternConfig instance */ PatternConfig.prototype.__addPartMeasurements = function (part, optional = false) { + // which list are we drawing from? const listType = optional ? 'optionalMeasurements' : 'measurements' + // if the part has measurements of this type, go through them if (part[listType]) { part[listType].forEach((m) => { + // we need to know what lists it's already present on const isInReqList = this.measurements.indexOf(m) !== -1 + // if it's already registered as required, we're done here + if (isInReqList) return + + // check if it's registered as optional const optInd = this.optionalMeasurements.indexOf(m) const isInOptList = optInd !== -1 - if (isInReqList) return + // if it is optional and not in the list, push it if (optional && !isInOptList) this.optionalMeasurements.push(m) + // if it's not optional if (!optional) { + // push it to required list this.measurements.push(m) + // make sure it's not also registered as optional if (isInOptList) this.optionalMeasurements.splice(optInd, 1) } @@ -199,7 +269,7 @@ PatternConfig.prototype.__addPartMeasurements = function (part, optional = false * * @private * @param {Part} part - The part of which to resolve the config - * @return {Pattern} this - The Pattern instance + * @return {PatternConfig} this - The PatternConfig instance */ PatternConfig.prototype.__addPartPlugins = function (part) { if (!part.plugins) return this @@ -255,109 +325,117 @@ PatternConfig.prototype.__addPartPlugins = function (part) { return this } -PatternConfig.prototype.__loadOptionDefault = function (optionName, option) { - this.pattern.settings.forEach((set) => { - if (typeof set.options[optionName] !== 'undefined') return - if (typeof option === 'object') { - if (typeof option.pct !== 'undefined') set.options[optionName] = option.pct / 100 - else if (typeof option.mm !== 'undefined') set.options[optionName] = option.mm - else if (typeof option.deg !== 'undefined') set.options[optionName] = option.deg - else if (typeof option.count !== 'undefined') set.options[optionName] = option.count - else if (typeof option.bool !== 'undefined') set.options[optionName] = option.bool - else if (typeof option.dflt !== 'undefined') set.options[optionName] = option.dflt - else { - let err = 'Unknown option type: ' + JSON.stringify(option) - this.store.log.error(err) - throw new Error(err) - } - } else set.options[optionName] = option - }) -} - -PatternConfig.prototype.__resolvePartDependencyChain = function (depChain, dependency, depType) { - const part = depChain[0] - - this.parts[dependency.name] = Object.freeze(dependency) - this.__addDependency('directDependencies', part, dependency) - - depChain.forEach((c) => this.__addDependency('resolvedDependencies', c, dependency)) - - switch (depType) { - case 'from': - this.__setFromHide(part, part.name, dependency.name) - this.inject[part.name] = dependency.name - break - case 'after': - this.__setAfterHide(part, part.name, dependency.name) - } -} - -PatternConfig.prototype.__resolveMutatedPartDistance = function (partName) { - const proposed_dependent_part_distance = this.__mutated.partDistance[partName] + 1 - let didChange = false - if (!this.directDependencies[partName]) return false - this.directDependencies[partName].forEach((dependency) => { - if ( - typeof this.__mutated.partDistance[dependency] === 'undefined' || - this.__mutated.partDistance[dependency] < proposed_dependent_part_distance - ) { - didChange = true - this.__mutated.partDistance[dependency] = proposed_dependent_part_distance - this.__resolveMutatedPartDistance(dependency) - } - if (DISTANCE_DEBUG) - this.store.log.debug( - `partDistance for \`${dependency}\` is __${this.__mutated.partDistance[dependency]}__` - ) - }) - - return didChange -} - +// the two types of dependencies const depTypes = ['from', 'after'] -PatternConfig.prototype.__resolvePartDependencies = function (depChain, distance) { - // Resolve part Dependencies. first from then after + +/** + * Recursively register part dependencies + * triggers {@link __addPart} on new parts found during resolution + * @param {Object[]} depChain an array starting with the current part to register and containing its dependents/descendents in order + * @return {PatternConfig} this + * @private + */ +PatternConfig.prototype.__resolvePartDependencies = function (depChain) { + // the current part is the head of the chain const part = depChain[0] + // get or make its array of resolved dependencies this.resolvedDependencies[part.name] = this.resolvedDependencies[part.name] || [] + // for each dependency type (from, after) depTypes.forEach((d) => { + // if the part has dependencies of that type if (part[d]) { if (DISTANCE_DEBUG) this.store.log.debug(`Processing \`${part.name}\` "${d}:"`) + // enforce an array const depsOfType = Array.isArray(part[d]) ? part[d] : [part[d]] + // each dependency depsOfType.forEach((dot) => { - let count = Object.keys(this.parts).length - // if any changes resulted from resolving this part mutation - this.__resolvePartDependencyChain(depChain, dot, d) - // if a new part was added, resolve the part - const newCount = Object.keys(this.parts).length - if (count < newCount) { - this.__resolvePart([dot, ...depChain], distance) - count = newCount + // add it as a direct dependency of the current part + this.__addDependency('directDependencies', part.name, dot.name) + // add it as a resolved dependency of all parts in the chain + depChain.forEach((c) => this.__addDependency('resolvedDependencies', c.name, dot.name)) + + // handle hiding and injecting + this.__handlePartDependencyOfType(part, dot.name, d) + + // if the dependency isn't registered, register it + if (!this.parts[dot.name]) { + // add the part's configuration + this.__addPart([dot, ...depChain]) } }) } }) + // now that the chain has been registered, recalculate the part distances this.__resolveMutatedPartDistance(part.name) } /** - * Adds a part as a simple dependency - * + * Adds a part as either a direct or a resolved dependency + * @param {string} dependencyList which list to add the part to, 'resolvedDependencies' or 'directDependencies' + * @param {string} partName the name of the part to add the dependency to in the list + * @param {string} depName the name of the dependency to add to the list * @private - * @param {string} name - The name of the dependency - * @param {object} part - The part configuration - * @param {object} dep - The dependency configuration - * @return {object} this - The Pattern instance */ -PatternConfig.prototype.__addDependency = function (dependencyList, part, dep) { - this[dependencyList][part.name] = this[dependencyList][part.name] || [] +PatternConfig.prototype.__addDependency = function (dependencyList, partName, depName) { + this[dependencyList][partName] = this[dependencyList][partName] || [] if (dependencyList == 'resolvedDependencies' && DISTANCE_DEBUG) - this.store.log.debug(`add ${dep.name} to ${part.name} dependencyResolution`) - if (this[dependencyList][part.name].indexOf(dep.name) === -1) - this[dependencyList][part.name].push(dep.name) + this.store.log.debug(`add ${depName} to ${partName} dependencyResolution`) + if (this[dependencyList][partName].indexOf(depName) === -1) + this[dependencyList][partName].push(depName) +} + +/** + * Handle dependency-type specific config business + * @param {Object} part the part to add the dependency to + * @param {string} depName the name of the dependency to add + * @param {string} depType the type of dependency, 'from' or 'after' + * @private + */ +PatternConfig.prototype.__handlePartDependencyOfType = function (part, depName, depType) { + switch (depType) { + case 'from': + this.__setFromHide(part, depName) + this.inject[part.name] = depName + break + case 'after': + this.__setAfterHide(part, depName) + } +} + +/** + * Resolve part option priority + * Recursively bumps priorities down the dependency chain + * @param {string} partName the name of the part to resolve + * @private + */ +PatternConfig.prototype.__resolveMutatedPartDistance = function (partName) { + // if the part has no dependencies, bail + if (!this.directDependencies[partName]) return + + // propose that each of the part's direct dependencies should be at a distance 1 further than the part's distance + const proposed_dependency_distance = this.__mutated.partDistance[partName] + 1 + // check each direct dependency + this.directDependencies[partName].forEach((dependency) => { + // if the dependency doesn't have a distance, or that distance is less than the proposal + if ( + typeof this.__mutated.partDistance[dependency] === 'undefined' || + this.__mutated.partDistance[dependency] < proposed_dependency_distance + ) { + // set the new distance + this.__mutated.partDistance[dependency] = proposed_dependency_distance + // bump the dependency's dependencies as well + this.__resolveMutatedPartDistance(dependency) + } + + if (DISTANCE_DEBUG) + this.store.log.debug( + `partDistance for \`${dependency}\` is __${this.__mutated.partDistance[dependency]}__` + ) + }) } /** @@ -376,24 +454,22 @@ PatternConfig.prototype.__resolveDraftOrder = function () { } /** - * Sets visibility of a dependency based on its config + * Sets visibility of a 'from' dependency based on its config * * @private * @param {Part} part - The part of which this is a dependency - * @param {string} name - The name of the part * @param {string} depName - The name of the dependency - * @param {int} set - The index of the set in the list of settings * @return {Pattern} this - The Pattern instance */ -PatternConfig.prototype.__setFromHide = function (part, name, depName) { +PatternConfig.prototype.__setFromHide = function (part, depName) { if ( part.hideDependencies || part.hideAll || - this.__mutated.partHide[name] || - this.__mutated.partHideAll[name] + this.partHide[part.name] || + this.partHideAll[part.name] ) { - this.__mutated.partHide[depName] = true - this.__mutated.partHideAll[depName] = true + this.partHide[depName] = true + this.partHideAll[depName] = true } return this @@ -404,15 +480,14 @@ PatternConfig.prototype.__setFromHide = function (part, name, depName) { * * @private * @param {Part} part - The part of which this is a dependency - * @param {string} name - The name of the part * @param {string} depName - The name of the dependency * @param {int} set - The index of the set in the list of settings * @return {Pattern} this - The Pattern instance */ -PatternConfig.prototype.__setAfterHide = function (part, name, depName) { - if (this.__mutated.partHide[name] || this.__mutated.partHideAll[name]) { - this.__mutated.partHide[depName] = true - this.__mutated.partHideAll[depName] = true +PatternConfig.prototype.__setAfterHide = function (part, depName) { + if (this.partHide[part.name] || this.partHideAll[part.name]) { + this.partHide[depName] = true + this.partHideAll[depName] = true } return this diff --git a/packages/core/src/store.mjs b/packages/core/src/store.mjs index db0bfbffeee..f88f0e5f343 100644 --- a/packages/core/src/store.mjs +++ b/packages/core/src/store.mjs @@ -38,6 +38,7 @@ export function Store(methods = []) { logs.warning.push(...data) }, error: function (...data) { + if (typeof window !== 'undefined') console.error(...data) logs.error.push(...data) }, } From a1e818eb95416693a510758d848a6ab9eedb6ead Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Thu, 23 Feb 2023 08:13:44 +0200 Subject: [PATCH 28/36] lint fixes and rename --- .../core/src/{patternConfig.mjs => pattern-config.mjs} | 9 ++++++--- packages/core/src/pattern.mjs | 8 ++------ packages/core/tests/pattern-init.test.mjs | 4 ++++ packages/core/tests/pattern-other.test.mjs | 9 ++++++--- 4 files changed, 18 insertions(+), 12 deletions(-) rename packages/core/src/{patternConfig.mjs => pattern-config.mjs} (97%) diff --git a/packages/core/src/patternConfig.mjs b/packages/core/src/pattern-config.mjs similarity index 97% rename from packages/core/src/patternConfig.mjs rename to packages/core/src/pattern-config.mjs index 79fa1083cb4..4dc0b91be08 100644 --- a/packages/core/src/patternConfig.mjs +++ b/packages/core/src/pattern-config.mjs @@ -9,8 +9,6 @@ import { __addNonEnumProp } from './utils.mjs' export function getPluginName(plugin) { const toCheck = Array.isArray(plugin) ? plugin[0] : plugin return toCheck.name || toCheck.plugin?.name || false - - return false } ///////////////// @@ -362,8 +360,13 @@ PatternConfig.prototype.__resolvePartDependencies = function (depChain) { // if the dependency isn't registered, register it if (!this.parts[dot.name]) { - // add the part's configuration + // add the part's configuration. this will recursively add the part's dependencies to all parts in the chain this.__addPart([dot, ...depChain]) + } else { + // if it's already registered, recursion won't happen, but we still need to add its resolved dependencies to all parts in the chain + this.resolvedDependencies[dot.name].forEach((r) => { + depChain.forEach((c) => this.__addDependency('resolvedDependencies', c.name, r)) + }) } }) } diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index 6c4ce537588..a3ba506cf0e 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -11,7 +11,7 @@ import { Store } from './store.mjs' import { Hooks } from './hooks.mjs' import { version } from '../data.mjs' import { __loadPatternDefaults } from './config.mjs' -import { PatternConfig, getPluginName } from './patternConfig.mjs' +import { PatternConfig, getPluginName } from './pattern-config.mjs' import cloneDeep from 'lodash.clonedeep' ////////////////////////////////////////////// @@ -814,11 +814,7 @@ Pattern.prototype.__needs = function (partName, set = 0) { // Walk the only parts, checking each one for a match in its dependencies for (const part of only) { if (part === partName) return true - if (this.config.resolvedDependencies[part]) { - for (const dependency of this.config.resolvedDependencies[part]) { - if (dependency === partName) return true - } - } + if (this.config.resolvedDependencies[part]?.indexOf(partName) !== -1) return true } return false diff --git a/packages/core/tests/pattern-init.test.mjs b/packages/core/tests/pattern-init.test.mjs index 036d91e89bd..384ec5aa5e5 100644 --- a/packages/core/tests/pattern-init.test.mjs +++ b/packages/core/tests/pattern-init.test.mjs @@ -238,6 +238,10 @@ describe('Pattern', () => { } }) + it( + 'Pattern.__init() should resolve nested dependencies for multiple parts that depend on the same part' + ) + // I am aware this does too much for one unit test, but this is to simplify TDD // we can split it up later it('Pattern.__init() should resolve nested injections', () => { diff --git a/packages/core/tests/pattern-other.test.mjs b/packages/core/tests/pattern-other.test.mjs index 3bdbb23448d..ba69d58a17d 100644 --- a/packages/core/tests/pattern-other.test.mjs +++ b/packages/core/tests/pattern-other.test.mjs @@ -54,11 +54,14 @@ describe('Pattern', () => { name: 'test', draft: ({ part }) => part, } - const design = new Design({ parts: [test] }) + const you = { + name: 'you', + draft: ({ part }) => part, + } + const design = new Design({ parts: [test, you] }) const pattern = new design({ only: ['you'] }) pattern.draft() - expect(pattern.setStores[0].logs.debug.length).to.equal(4) - expect(pattern.setStores[0].logs.debug[3]).to.equal( + expect(pattern.setStores[0].logs.debug).to.include( 'Part `test` is not needed. Skipping draft and setting hidden to `true`' ) }) From 12ffb0a9c1499b10887c016d93509c21a565299b Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Thu, 23 Feb 2023 18:22:22 +0200 Subject: [PATCH 29/36] start of draft queue class --- packages/core/src/pattern-config.mjs | 10 ++--- packages/core/src/pattern-draft-queue.mjs | 42 +++++++++++++++++++ packages/core/src/pattern.mjs | 15 ++++--- .../core/tests/pattern-runtime-parts.test.mjs | 5 +++ 4 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 packages/core/src/pattern-draft-queue.mjs diff --git a/packages/core/src/pattern-config.mjs b/packages/core/src/pattern-config.mjs index 4dc0b91be08..2f6e0f6a94b 100644 --- a/packages/core/src/pattern-config.mjs +++ b/packages/core/src/pattern-config.mjs @@ -65,7 +65,7 @@ const DISTANCE_DEBUG = false * @param {Object} part a part configuration * @return {boolean} whether the part is valid */ -PatternConfig.prototype.validatePart = function (part) { +PatternConfig.prototype.isPartValid = function (part) { if (typeof part?.draft !== 'function') { this.store.log.error(`Part must have a draft() method`) return false @@ -84,7 +84,7 @@ PatternConfig.prototype.validatePart = function (part) { * @param {Object} part */ PatternConfig.prototype.addPart = function (part) { - if (this.validatePart(part)) this.__addPart([part]) + if (this.isPartValid(part)) this.__addPart([part]) return this } @@ -134,9 +134,9 @@ PatternConfig.prototype.__addPart = function (depChain) { const part = depChain[0] // the longer the chain, the deeper the part is down it const distance = depChain.length - if (!this.parts[part.name]) { - this.parts[part.name] = Object.freeze(part) - } + if (!this.parts[part.name]) this.parts[part.name] = Object.freeze(part) + else return + // if it hasn't been registered with a distance, do that now if (typeof this.__mutated.partDistance[part.name] === 'undefined') { this.__mutated.partDistance[part.name] = distance diff --git a/packages/core/src/pattern-draft-queue.mjs b/packages/core/src/pattern-draft-queue.mjs new file mode 100644 index 00000000000..db8355459a4 --- /dev/null +++ b/packages/core/src/pattern-draft-queue.mjs @@ -0,0 +1,42 @@ +export function PatternDraftQueue(pattern) { + this.__configResolver = pattern.__configResolver + this.queue = this.__resolveDraftOrder() + this.start() +} + +PatternDraftQueue.prototype.start = function () { + this.queueIndex = 0 +} + +PatternDraftQueue.prototype.addPart = function (partName) { + this.queue.push(partName) + return this +} + +PatternDraftQueue.prototype.hasNext = function () { + return this.queueIndex < this.queue.length +} + +PatternDraftQueue.prototype.peek = function () { + return this.queue[this.queueIndex] +} + +PatternDraftQueue.prototype.next = function () { + const next = this.peek() + this.queueIndex++ + return next +} + +/** + * Resolves the draft order based on the configuation + * + * @private + * @param {object} graph - The object of resolved dependencies, used to call itself recursively + * @return {Pattern} this - The Pattern instance + */ +PatternDraftQueue.prototype.__resolveDraftOrder = function () { + const partDistances = this.__configResolver.__mutated.partDistance + return Object.keys(this.__configResolver.parts).sort( + (p1, p2) => partDistances[p2] - partDistances[p1] + ) +} diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index a3ba506cf0e..33b260007c7 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -12,6 +12,7 @@ import { Hooks } from './hooks.mjs' import { version } from '../data.mjs' import { __loadPatternDefaults } from './config.mjs' import { PatternConfig, getPluginName } from './pattern-config.mjs' +import { PatternDraftQueue } from './pattern-draft-queue.mjs' import cloneDeep from 'lodash.clonedeep' ////////////////////////////////////////////// @@ -67,10 +68,12 @@ export function Pattern(designConfig = {}) { * @return {object} this - The Pattern instance */ Pattern.prototype.addPart = function (part, resolveImmediately = false) { - if (this.__configResolver.validatePart(part) && this.designConfig.parts.indexOf(part) === -1) { + if (this.__configResolver.isPartValid(part) && this.designConfig.parts.indexOf(part) === -1) { this.designConfig.parts.push(part) - if (resolveImmediately) this.__configResolver.addPart(part) - else this.__initialized = false + if (resolveImmediately) { + if (this.__configResolver.addPart(part) && typeof this.draftQueue !== 'undefined') + this.draftQueue.addPart(part.name) + } else this.__initialized = false } return this } @@ -82,6 +85,7 @@ Pattern.prototype.addPart = function (part, resolveImmediately = false) { */ Pattern.prototype.draft = function () { this.__init() + this.draftQueue = new PatternDraftQueue(this) this.__runHooks('preDraft') // Keep container for drafted parts fresh this.parts = [] @@ -100,8 +104,9 @@ Pattern.prototype.draft = function () { // Handle snap for pct options this.__loadAbsoluteOptionsSet(set) - for (const partName of this.config.draftOrder) { - this.createPartForSet(partName, set) + this.draftQueue.start() + while (this.draftQueue.hasNext()) { + this.createPartForSet(this.draftQueue.next(), set) } this.__runHooks('postSetDraft') } diff --git a/packages/core/tests/pattern-runtime-parts.test.mjs b/packages/core/tests/pattern-runtime-parts.test.mjs index dbe71799dbb..4d8587b1142 100644 --- a/packages/core/tests/pattern-runtime-parts.test.mjs +++ b/packages/core/tests/pattern-runtime-parts.test.mjs @@ -22,6 +22,11 @@ describe('Pattern', () => { draft: ({ part }) => part, } + describe('with runtime: true, resolveImmediately: true', () => { + it('adds the part to the current draft cycle') + it('does not add the part to subsequent draft cycles') + }) + describe('with resolveImmediately: true', () => { it('Should add the part to parts object', () => { const design = new Design({ parts: [part1] }) From dc998d4e985420cb2fdedcb039e9f763298b79a7 Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Thu, 23 Feb 2023 19:15:20 +0200 Subject: [PATCH 30/36] add sinon for better testing --- config/dependencies.yaml | 3 + packages/core/package.json | 5 +- packages/core/src/pattern-draft-queue.mjs | 6 +- packages/core/src/pattern.mjs | 7 +- .../core/tests/pattern-runtime-parts.test.mjs | 51 ++++++++++++-- yarn.lock | 67 ++++++++++++++++++- 6 files changed, 128 insertions(+), 11 deletions(-) diff --git a/config/dependencies.yaml b/config/dependencies.yaml index d9d7109c0da..b991c64b10e 100644 --- a/config/dependencies.yaml +++ b/config/dependencies.yaml @@ -62,6 +62,9 @@ core: dev: 'eslint': &eslint '8.34.0' 'nyc': '15.1.0' + 'mocha': *mocha + 'chai': *chai + 'sinon': &sinon '^15.0.1' diana: peer: '@freesewing/brian': *freesewing diff --git a/packages/core/package.json b/packages/core/package.json index e6ff0e2940f..bb3a265071c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -60,7 +60,10 @@ }, "devDependencies": { "eslint": "8.34.0", - "nyc": "15.1.0" + "nyc": "15.1.0", + "mocha": "10.0.0", + "chai": "4.2.0", + "sinon": "^15.0.1" }, "files": [ "dist/*", diff --git a/packages/core/src/pattern-draft-queue.mjs b/packages/core/src/pattern-draft-queue.mjs index db8355459a4..30014d57f42 100644 --- a/packages/core/src/pattern-draft-queue.mjs +++ b/packages/core/src/pattern-draft-queue.mjs @@ -9,7 +9,7 @@ PatternDraftQueue.prototype.start = function () { } PatternDraftQueue.prototype.addPart = function (partName) { - this.queue.push(partName) + if (!this.contains(partName)) this.queue.push(partName) return this } @@ -27,6 +27,10 @@ PatternDraftQueue.prototype.next = function () { return next } +PatternDraftQueue.prototype.contains = function (partName) { + return this.queue.indexOf(partName) !== -1 +} + /** * Resolves the draft order based on the configuation * diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index 33b260007c7..5eefc19056f 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -67,8 +67,11 @@ export function Pattern(designConfig = {}) { * It might be useful to not resolve immediately if a number of parts will be added over multiple calls * @return {object} this - The Pattern instance */ -Pattern.prototype.addPart = function (part, resolveImmediately = false) { - if (this.__configResolver.isPartValid(part) && this.designConfig.parts.indexOf(part) === -1) { +Pattern.prototype.addPart = function (part, resolveImmediately = true) { + if ( + this.__configResolver.isPartValid(part) && + !this.designConfig.parts.find((p) => p.name == part.name) + ) { this.designConfig.parts.push(part) if (resolveImmediately) { if (this.__configResolver.addPart(part) && typeof this.draftQueue !== 'undefined') diff --git a/packages/core/tests/pattern-runtime-parts.test.mjs b/packages/core/tests/pattern-runtime-parts.test.mjs index 4d8587b1142..f272afcf495 100644 --- a/packages/core/tests/pattern-runtime-parts.test.mjs +++ b/packages/core/tests/pattern-runtime-parts.test.mjs @@ -1,5 +1,6 @@ import chai from 'chai' import { Design } from '../src/index.mjs' +import sinon from 'sinon' const expect = chai.expect @@ -22,11 +23,6 @@ describe('Pattern', () => { draft: ({ part }) => part, } - describe('with runtime: true, resolveImmediately: true', () => { - it('adds the part to the current draft cycle') - it('does not add the part to subsequent draft cycles') - }) - describe('with resolveImmediately: true', () => { it('Should add the part to parts object', () => { const design = new Design({ parts: [part1] }) @@ -152,6 +148,51 @@ describe('Pattern', () => { pattern.addPart(part3, true) expect(pattern.config.options.opt1).to.equal(opt1) }) + + describe('during drafting', () => { + it('adds the part to the draft queue', () => { + const design = new Design({ parts: [part1] }) + const pattern = new design() + pattern.use({ + name: 'draftTimePartPlugin', + hooks: { + postPartDraft: (pattern) => { + const newPart = { + name: 'newPartTest', + draft: ({ part }) => part, + } + + pattern.addPart(newPart) + }, + }, + }) + + pattern.draft() + expect(pattern.draftQueue.contains('newPartTest')).to.be.true + }) + it('drafts the part', () => { + const design = new Design({ parts: [part1] }) + const pattern = new design() + const part2Draft = ({ part }) => part + const draftSpy = sinon.spy(part2Draft) + pattern.use({ + name: 'draftTimePartPlugin', + hooks: { + postPartDraft: (pattern) => { + const newPart = { + name: 'newPartTest', + draft: draftSpy, + } + + pattern.addPart(newPart) + }, + }, + }) + + pattern.draft() + expect(draftSpy.calledOnce).to.be.true + }) + }) }) describe('with resolveImmediately: false', () => { diff --git a/yarn.lock b/yarn.lock index ef9769e133b..ca701533563 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4750,6 +4750,34 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== +"@sinonjs/commons@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" + integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@10.0.2", "@sinonjs/fake-timers@^10.0.2": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz#d10549ed1f423d80639c528b6c7f5a1017747d0c" + integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw== + dependencies: + "@sinonjs/commons" "^2.0.0" + +"@sinonjs/samsam@^7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-7.0.1.tgz#5b5fa31c554636f78308439d220986b9523fc51f" + integrity sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw== + dependencies: + "@sinonjs/commons" "^2.0.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + +"@sinonjs/text-encoding@^0.7.1": + version "0.7.2" + resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz#5981a8db18b56ba38ef0efb7d995b12aa7b51918" + integrity sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ== + "@socket.io/component-emitter@~3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" @@ -12259,6 +12287,11 @@ just-diff@^5.0.1: resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-5.0.3.tgz#4c9c514dec5526b25ab977590e3c39a0cf271554" integrity sha512-a8p80xcpJ6sdurk5PxDKb4mav9MeKjA3zFKZpCWBIfvg8mznfnmb13MKZvlrwJ+Lhis0wM3uGAzE0ArhFHvIcg== +just-extend@^4.0.2: + version "4.2.1" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" + integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== + jwa@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" @@ -14337,6 +14370,17 @@ next@13.1.6: "@next/swc-win32-ia32-msvc" "13.1.6" "@next/swc-win32-x64-msvc" "13.1.6" +nise@^5.1.2: + version "5.1.4" + resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.4.tgz#491ce7e7307d4ec546f5a659b2efe94a18b4bbc0" + integrity sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg== + dependencies: + "@sinonjs/commons" "^2.0.0" + "@sinonjs/fake-timers" "^10.0.2" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + path-to-regexp "^1.7.0" + nlcst-to-string@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/nlcst-to-string/-/nlcst-to-string-2.0.4.tgz#9315dfab80882bbfd86ddf1b706f53622dc400cc" @@ -15535,6 +15579,13 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -18346,6 +18397,18 @@ simple-wcswidth@^1.0.1: resolved "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz#8ab18ac0ae342f9d9b629604e54d2aa1ecb018b2" integrity sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg== +sinon@^15.0.1: + version "15.0.1" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-15.0.1.tgz#ce062611a0b131892e2c18f03055b8eb6e8dc234" + integrity sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg== + dependencies: + "@sinonjs/commons" "^2.0.0" + "@sinonjs/fake-timers" "10.0.2" + "@sinonjs/samsam" "^7.0.1" + diff "^5.0.0" + nise "^5.1.2" + supports-color "^7.2.0" + sirv@^1.0.7: version "1.0.19" resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" @@ -19131,7 +19194,7 @@ supports-color@^5.3.0, supports-color@^5.5.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -19650,7 +19713,7 @@ type-component@0.0.1: resolved "https://registry.yarnpkg.com/type-component/-/type-component-0.0.1.tgz#952a6c81c21efd24d13d811d0c8498cb860e1956" integrity sha512-mDZRBQS2yZkwRQKfjJvQ8UIYJeBNNWCq+HBNstl9N5s9jZ4dkVYXEGkVPsSCEh5Ld4JM1kmrZTzjnrqSAIQ7dw== -type-detect@^4.0.0, type-detect@^4.0.5: +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== From 6ba2e4e88560c78e92ed965da3e6ddc56dbd8ab4 Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Mon, 27 Feb 2023 18:58:47 -0600 Subject: [PATCH 31/36] change to bin-pack-with-constraints --- config/dependencies.yaml | 2 +- packages/core/package.json | 2 +- packages/core/src/pattern.mjs | 5 +++-- yarn.lock | 8 ++++---- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/config/dependencies.yaml b/config/dependencies.yaml index b991c64b10e..faf138ae427 100644 --- a/config/dependencies.yaml +++ b/config/dependencies.yaml @@ -53,7 +53,7 @@ charlie: core: _: 'bezier-js': '6.1.0' - 'bin-pack': '1.0.2' + 'bin-pack-with-constraints': '1.0.1' 'hooks': '0.3.2' 'lodash.get': &_get '4.4.2' 'lodash.set': &_set '4.3.2' diff --git a/packages/core/package.json b/packages/core/package.json index bb3a265071c..44dec9d3660 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -51,7 +51,7 @@ "peerDependencies": {}, "dependencies": { "bezier-js": "6.1.0", - "bin-pack": "1.0.2", + "bin-pack-with-constraints": "1.0.1", "hooks": "0.3.2", "lodash.get": "4.4.2", "lodash.set": "4.3.2", diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index 5eefc19056f..ee6995ff81a 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -1,5 +1,5 @@ import { Attributes } from './attributes.mjs' -import pack from 'bin-pack' +import pack from 'bin-pack-with-constraints' import { __addNonEnumProp, __macroName } from './utils.mjs' import { Part } from './part.mjs' import { Stack } from './stack.mjs' @@ -72,6 +72,7 @@ Pattern.prototype.addPart = function (part, resolveImmediately = true) { this.__configResolver.isPartValid(part) && !this.designConfig.parts.find((p) => p.name == part.name) ) { + this.store.log.debug(`Adding Part \`${part.name}\` at runtime`) this.designConfig.parts.push(part) if (resolveImmediately) { if (this.__configResolver.addPart(part) && typeof this.draftQueue !== 'undefined') @@ -922,7 +923,7 @@ Pattern.prototype.__pack = function () { } } if (this.settings[0].layout === true) { - let size = pack(bins, { inPlace: true }) + let size = pack(bins, { inPlace: true, maxWidth: this.settings[0].maxWidth }) for (let bin of bins) { this.autoLayout.stacks[bin.id] = { move: {} } let stack = this.stacks[bin.id] diff --git a/yarn.lock b/yarn.lock index ca701533563..fbc7551fec3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6254,10 +6254,10 @@ bin-links@^3.0.0: rimraf "^3.0.0" write-file-atomic "^4.0.0" -bin-pack@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bin-pack/-/bin-pack-1.0.2.tgz#c2a014edbf0bed70a3292062ed46577b96120679" - integrity sha512-aOk0SxEon5LF9cMxQFViSKb4qccG6rs7XKyMXIb1J8f8LA2acTIWnHdT0IOTe4gYBbqgjdbuTZ5f+UP+vlh4Mw== +bin-pack-with-constraints@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bin-pack-with-constraints/-/bin-pack-with-constraints-1.0.1.tgz#47e67b481724a8a5a3644ec9c9ccf881c2725096" + integrity sha512-fiPxvZAWuIqLpK79Ov58PztT/ZiGbpDB7usifleilJ/4aqhSx0ivY5kRifESJnR2rq51pScbUJ0LgCebufGJnA== binary-extensions@^2.0.0: version "2.2.0" From b419e6ce16684a3924179a1842df49b79ce1eb9d Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Tue, 28 Feb 2023 10:47:56 -0600 Subject: [PATCH 32/36] refactor and test part hiding --- packages/core/src/pattern-config.mjs | 38 ++-- packages/core/src/pattern.mjs | 3 - packages/core/tests/pattern-init.test.mjs | 221 +++++++++++++++++++++- 3 files changed, 237 insertions(+), 25 deletions(-) diff --git a/packages/core/src/pattern-config.mjs b/packages/core/src/pattern-config.mjs index 2f6e0f6a94b..3c4cdc0bb88 100644 --- a/packages/core/src/pattern-config.mjs +++ b/packages/core/src/pattern-config.mjs @@ -41,16 +41,18 @@ export function PatternConfig(pattern) { this.parts = {} /** @type {Object} which parts are hidden */ this.partHide = {} - /** @type {Object} which parts hide all their dependencies */ - this.partHideAll = {} - /** to track which parts have already been resolved */ - __addNonEnumProp(this, '__resolvedParts', {}) /** @type {Object} to track when to overwrite options */ __addNonEnumProp(this, '__mutated', { optionDistance: {}, partDistance: {}, }) + + /** @type {Object} tracking for dependency hiding */ + __addNonEnumProp(this, '__hiding', { + all: {}, + deps: {}, + }) } /** @type {Boolean} change me to true to get full debugging of the resolution process */ @@ -101,7 +103,7 @@ PatternConfig.prototype.logPartDistances = function () { /** * Return a configuration in the structure expected by the pattern - * @return {Object} contains parts, plugins, measurements, options, optionalMeasurements, resolvedDependencies, directDependencies, inject, draftOrder, partHide, and partHideAll + * @return {Object} contains parts, plugins, measurements, options, optionalMeasurements, resolvedDependencies, directDependencies, inject, draftOrder, partHide */ PatternConfig.prototype.asConfig = function () { return { @@ -115,7 +117,6 @@ PatternConfig.prototype.asConfig = function () { inject: this.inject, draftOrder: this.__resolveDraftOrder(), partHide: this.partHide, - partHideAll: this.partHideAll, } } @@ -147,9 +148,11 @@ PatternConfig.prototype.__addPart = function (depChain) { ) } - // Hide when hideAll is set + // Handle various hiding possibilities + if (part.hide || part.hideAll) this.partHide[part.name] = true + if (part.hideDependencies) this.__hiding.deps[part.name] = true if (part.hideAll) { - this.partHide[part.name] = true + this.__hiding.all[part.name] = true } // resolve its dependencies @@ -167,9 +170,6 @@ PatternConfig.prototype.__addPart = function (depChain) { * @return this */ PatternConfig.prototype.__addPartConfig = function (part) { - // don't resolve a part that's already been resolved - if (this.__resolvedParts[part.name]) return this - return this.__addPartOptions(part) // add options .__addPartMeasurements(part, false) // add required measurements .__addPartMeasurements(part, true) // add optional measurements @@ -465,14 +465,13 @@ PatternConfig.prototype.__resolveDraftOrder = function () { * @return {Pattern} this - The Pattern instance */ PatternConfig.prototype.__setFromHide = function (part, depName) { - if ( - part.hideDependencies || - part.hideAll || - this.partHide[part.name] || - this.partHideAll[part.name] - ) { + if (part.hideDependencies || this.__hiding.deps[part.name]) { this.partHide[depName] = true - this.partHideAll[depName] = true + this.__hiding.deps[depName] = true + } + if (part.hideAll || this.__hiding.all[part.name]) { + this.partHide[depName] = true + this.__hiding.all[depName] = true } return this @@ -488,9 +487,8 @@ PatternConfig.prototype.__setFromHide = function (part, depName) { * @return {Pattern} this - The Pattern instance */ PatternConfig.prototype.__setAfterHide = function (part, depName) { - if (this.partHide[part.name] || this.partHideAll[part.name]) { + if (this.__hiding.all[part.name]) { this.partHide[depName] = true - this.partHideAll[depName] = true } return this diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index ee6995ff81a..70688304860 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -500,10 +500,7 @@ Pattern.prototype.__isPartHidden = function (partName) { if (Array.isArray(this.settings[this.activeSet || 0].only)) { if (this.settings[this.activeSet || 0].only.includes(partName)) return false } - if (this.config.parts?.[partName]?.hide) return true - if (this.config.parts?.[partName]?.hideAll) return true if (this.config.partHide?.[partName]) return true - if (this.config.partHideAll?.[partName]) return true if (this.parts?.[this.activeSet]?.[partName]?.hidden) return true return false diff --git a/packages/core/tests/pattern-init.test.mjs b/packages/core/tests/pattern-init.test.mjs index 384ec5aa5e5..e5147351666 100644 --- a/packages/core/tests/pattern-init.test.mjs +++ b/packages/core/tests/pattern-init.test.mjs @@ -589,7 +589,7 @@ describe('Pattern', () => { expect(pattern.hooks.preRender.length).to.equal(2) }) - it('Pattern.__init() should load conditional plugin', () => { + it('Pattern.__init() should load conditional plugin if condition is met', () => { const plugin = { name: 'example', version: 1, @@ -611,7 +611,7 @@ describe('Pattern', () => { expect(pattern.hooks.preRender.length).to.equal(1) }) - it('Pattern.__init() should not load conditional plugin', () => { + it('Pattern.__init() should not load conditional plugin if condition is not mett', () => { const plugin = { name: 'example', version: 1, @@ -667,6 +667,38 @@ describe('Pattern', () => { expect(pattern.hooks.preRender.length).to.equal(1) }) + it('Pattern.__init() should load a conditional plugin multiple times with different conditions', () => { + const plugin1 = { + name: 'example1', + version: 1, + hooks: { + preRender: function (svg) { + svg.attributes.add('freesewing:plugin-example', 1) + }, + }, + } + + const condition1 = () => true + const condition2 = () => false + const part = { + name: 'test.part', + plugins: [{ plugin: plugin1, condition: condition1 }], + draft: (part) => part, + } + const part2 = { + name: 'test.part2', + plugins: [{ plugin: plugin1, condition: condition2 }], + draft: (part) => part, + } + const design = new Design({ parts: [part, part2] }) + const pattern = new design() + pattern.__init() + expect(pattern.config.plugins).to.be.an('object').that.has.all.keys('example1', 'example1_') + expect(pattern.config.plugins.example1.plugin).to.deep.equal(plugin1) + expect(pattern.config.plugins.example1_.plugin).to.deep.equal(plugin1) + expect(pattern.hooks.preRender.length).to.equal(1) + }) + it('Load conditional plugins that are also passing data', () => { const plugin1 = { name: 'example1', @@ -760,6 +792,191 @@ describe('Pattern', () => { expect(count).to.equal(2) }) + describe('Hiding parts', () => { + const blankDraft = ({ part }) => part + const afterPart = { + name: 'afterPart', + draft: blankDraft, + } + const fromPart = { + name: 'fromPart', + draft: blankDraft, + } + describe('{hide: true}', () => { + const mainPart = { + name: 'mainPart', + after: afterPart, + from: fromPart, + hide: true, + draft: blankDraft, + } + + const Test = new Design({ + name: 'test', + parts: [mainPart], + }) + + const pattern = new Test() + pattern.__init() + + it('Should hide the part', () => { + expect(pattern.__isPartHidden('mainPart')).to.be.true + }) + + it("Should not hide the part's dependencies", () => { + expect(pattern.__isPartHidden('fromPart')).to.be.false + expect(pattern.__isPartHidden('afterPart')).to.be.false + }) + + describe('Nested Parts', () => { + const mainPart = { + name: 'mainPart', + after: afterPart, + from: fromPart, + draft: blankDraft, + } + const grandChild = { + name: 'grandChild', + from: mainPart, + hide: true, + draft: blankDraft, + } + const Test = new Design({ + name: 'test', + parts: [grandChild], + }) + + const pattern = new Test() + pattern.__init() + + it('should not hide nested `from` dependencies', () => { + expect(pattern.__isPartHidden('fromPart')).to.be.false + expect(pattern.__isPartHidden('mainPart')).to.be.false + }) + + it('should not hide nested `after` dependencies', () => { + expect(pattern.__isPartHidden('afterPart')).to.be.false + }) + }) + }) + + describe('{hideDependencies: true}', () => { + const mainPart = { + name: 'mainPart', + hideDependencies: true, + after: afterPart, + from: fromPart, + draft: blankDraft, + } + const Test = new Design({ + name: 'test', + parts: [mainPart], + }) + + const pattern = new Test() + pattern.__init() + + it('Should not hide the part', () => { + expect(pattern.__isPartHidden('mainPart')).to.be.false + }) + it("Should hide the part's `from` dependencies", () => { + expect(pattern.__isPartHidden('fromPart')).to.be.true + }) + it("Should not hide the part's `after` dependencies", () => { + expect(pattern.__isPartHidden('afterPart')).to.be.false + }) + + describe('Nested Parts', () => { + const mainPart = { + name: 'mainPart', + after: afterPart, + from: fromPart, + draft: blankDraft, + } + const grandChild = { + name: 'grandChild', + from: mainPart, + hideDependencies: true, + draft: blankDraft, + } + const Test = new Design({ + name: 'test', + parts: [grandChild], + }) + + const pattern = new Test() + pattern.__init() + + it('should hide nested `from` dependencies', () => { + expect(pattern.__isPartHidden('fromPart')).to.be.true + expect(pattern.__isPartHidden('mainPart')).to.be.true + }) + + it('should not hide nested `after` dependencies', () => { + expect(pattern.__isPartHidden('afterPart')).to.be.false + }) + }) + }) + + describe('{hideAll: true}', () => { + const mainPart = { + name: 'mainPart', + hideAll: true, + after: afterPart, + from: fromPart, + draft: blankDraft, + } + const Test = new Design({ + name: 'test', + parts: [mainPart], + }) + + const pattern = new Test() + pattern.__init() + + it('Should hide the part', () => { + expect(pattern.__isPartHidden('mainPart')).to.be.true + }) + it("Should hide the part's `from` dependencies", () => { + expect(pattern.__isPartHidden('fromPart')).to.be.true + }) + it("Should hide the part's `after` dependencies", () => { + expect(pattern.__isPartHidden('afterPart')).to.be.true + }) + + describe('Nested Parts', () => { + const mainPart = { + name: 'mainPart', + after: afterPart, + from: fromPart, + draft: blankDraft, + } + const grandChild = { + name: 'grandChild', + from: mainPart, + hideAll: true, + draft: blankDraft, + } + const Test = new Design({ + name: 'test', + parts: [grandChild], + }) + + const pattern = new Test() + pattern.__init() + + it('should hide nested `from` dependencies', () => { + expect(pattern.__isPartHidden('fromPart')).to.be.true + expect(pattern.__isPartHidden('mainPart')).to.be.true + }) + + it('should hide nested `after` dependencies', () => { + expect(pattern.__isPartHidden('afterPart')).to.be.true + }) + }) + }) + }) + it('Should check whether created parts get the pattern context', () => { let partContext const plugin = { From f5725cbfcab00fd26f2f54b18cda41d5dd6b016b Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Tue, 28 Feb 2023 12:23:15 -0600 Subject: [PATCH 33/36] more documentation --- packages/core/src/pattern-draft-queue.mjs | 37 +++++++++++++++++++++-- packages/core/src/pattern.mjs | 6 ++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/core/src/pattern-draft-queue.mjs b/packages/core/src/pattern-draft-queue.mjs index 30014d57f42..96daeb8b40b 100644 --- a/packages/core/src/pattern-draft-queue.mjs +++ b/packages/core/src/pattern-draft-queue.mjs @@ -1,42 +1,73 @@ +/** + * A queue for handling the draft order of pattern parts + * Unlike most queues, traversing this queue is non-destructive + * so that the queue can be traversed many times. + * The goal is to allow the queue to be manipulated while being traversed + * @class + * @param {Pattern} pattern the pattern that will use the queue + */ export function PatternDraftQueue(pattern) { + // save the config resolver this.__configResolver = pattern.__configResolver + // get the draft order in its current state this.queue = this.__resolveDraftOrder() + // start at 0 this.start() } +/** Go back to the beginning of the queue */ PatternDraftQueue.prototype.start = function () { this.queueIndex = 0 } +/** + * Add a part to end of the queue. Useful for queueing up parts a draft time + * @param {string} partName the name to the part to add + */ PatternDraftQueue.prototype.addPart = function (partName) { if (!this.contains(partName)) this.queue.push(partName) return this } +/** + * Check whether the queue has a next part without moving the queue index + * @return {Boolean} whether there is a next part in the queue + */ PatternDraftQueue.prototype.hasNext = function () { return this.queueIndex < this.queue.length } +/** + * Get the next part in the queue without moving the queue index + * @return {string} the next part in the queue + */ PatternDraftQueue.prototype.peek = function () { return this.queue[this.queueIndex] } +/** + * Get the next part in the queue and move the queue index + * @return {string} the next part in the queue + */ PatternDraftQueue.prototype.next = function () { const next = this.peek() this.queueIndex++ return next } +/** + * Check whether a part is already queued + * @param {string} partName the name of the part + * @return {boolean} whether the part is in the queue + */ PatternDraftQueue.prototype.contains = function (partName) { return this.queue.indexOf(partName) !== -1 } /** * Resolves the draft order based on the configuation - * * @private - * @param {object} graph - The object of resolved dependencies, used to call itself recursively - * @return {Pattern} this - The Pattern instance + * @return A list of parts in the order they should be drafted */ PatternDraftQueue.prototype.__resolveDraftOrder = function () { const partDistances = this.__configResolver.__mutated.partDistance diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index 70688304860..e74dcddefd3 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -920,6 +920,7 @@ Pattern.prototype.__pack = function () { } } if (this.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: this.settings[0].maxWidth }) for (let bin of bins) { this.autoLayout.stacks[bin.id] = { move: {} } @@ -950,6 +951,11 @@ Pattern.prototype.__pack = function () { return this } +/** + * Gets the configuration for the config resolver and sets it on the pattern + * @private + * @return {Pattern} this - The Pattern instance + */ Pattern.prototype.__resolveConfig = function () { this.config = this.__configResolver.asConfig() return this From a68f3678ea3fd56363c37027ba58fae632cdda8f Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Tue, 28 Feb 2023 12:24:54 -0600 Subject: [PATCH 34/36] bit of cleanup --- packages/core/src/pattern.mjs | 2 -- yarn.lock | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index e74dcddefd3..320611aa6cb 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -965,8 +965,6 @@ Pattern.prototype.__resolveConfig = function () { * Resolves parts and their dependencies * * @private - * @param {int} count - The count is used to call itself recursively - * @param {int} distance - Keeps track of how far the dependency is from the pattern * @return {Pattern} this - The Pattern instance */ Pattern.prototype.__resolveParts = function () { diff --git a/yarn.lock b/yarn.lock index fbc7551fec3..b39320158a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12966,7 +12966,7 @@ lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.13.1.tgz#267a81fbd0881327c46a81c5922606a2cfe336c4" integrity sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ== -luxon@3.2.1: +luxon@latest: version "3.2.1" resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.2.1.tgz#14f1af209188ad61212578ea7e3d518d18cee45f" integrity sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg== From 714a1383ba77640381b4f62d87960521b7f3eceb Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Wed, 1 Mar 2023 08:44:55 -0600 Subject: [PATCH 35/36] a bit more testing and cleanup --- packages/core/src/pattern-config.mjs | 5 ++-- packages/core/tests/pattern-init.test.mjs | 28 ++++++++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/packages/core/src/pattern-config.mjs b/packages/core/src/pattern-config.mjs index 3c4cdc0bb88..020e698dce8 100644 --- a/packages/core/src/pattern-config.mjs +++ b/packages/core/src/pattern-config.mjs @@ -465,11 +465,11 @@ PatternConfig.prototype.__resolveDraftOrder = function () { * @return {Pattern} this - The Pattern instance */ PatternConfig.prototype.__setFromHide = function (part, depName) { - if (part.hideDependencies || this.__hiding.deps[part.name]) { + if (this.__hiding.deps[part.name]) { this.partHide[depName] = true this.__hiding.deps[depName] = true } - if (part.hideAll || this.__hiding.all[part.name]) { + if (this.__hiding.all[part.name]) { this.partHide[depName] = true this.__hiding.all[depName] = true } @@ -489,6 +489,7 @@ PatternConfig.prototype.__setFromHide = function (part, depName) { PatternConfig.prototype.__setAfterHide = function (part, depName) { if (this.__hiding.all[part.name]) { this.partHide[depName] = true + this.__hiding.all[depName] = true } return this diff --git a/packages/core/tests/pattern-init.test.mjs b/packages/core/tests/pattern-init.test.mjs index e5147351666..8a3e4d21f77 100644 --- a/packages/core/tests/pattern-init.test.mjs +++ b/packages/core/tests/pattern-init.test.mjs @@ -238,9 +238,31 @@ describe('Pattern', () => { } }) - it( - 'Pattern.__init() should resolve nested dependencies for multiple parts that depend on the same part' - ) + it('Pattern.__init() should resolve nested dependencies for multiple parts that depend on the same part', () => { + const partD = { + name: 'test.partD', + from: partB, + draft: ({ part }) => part, + } + + const Pattern = new Design({ + data: { + name: 'test', + version: '1.2.3', + }, + parts: [partC, partD], + }) + const pattern = new Pattern() + pattern.__init() + expect(pattern.config.resolvedDependencies['test.partD']).to.have.members([ + 'test.partA', + 'test.partB', + ]) + expect(pattern.config.resolvedDependencies['test.partC']).to.have.members([ + 'test.partA', + 'test.partB', + ]) + }) // I am aware this does too much for one unit test, but this is to simplify TDD // we can split it up later From 503277f9ea2d466db4012fc3ebb7b156c96766b4 Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Fri, 3 Mar 2023 11:38:35 -0600 Subject: [PATCH 36/36] fix part hiding for circumstances with only --- packages/core/src/pattern.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index 320611aa6cb..e01f3cdbd60 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -497,13 +497,13 @@ Pattern.prototype.__init = function () { * @return {bool} hidden - true if the part is hidden, or false if not */ Pattern.prototype.__isPartHidden = function (partName) { + const partHidden = this.parts?.[this.activeSet]?.[partName]?.hidden || false if (Array.isArray(this.settings[this.activeSet || 0].only)) { - if (this.settings[this.activeSet || 0].only.includes(partName)) return false + if (this.settings[this.activeSet || 0].only.includes(partName)) return partHidden } if (this.config.partHide?.[partName]) return true - if (this.parts?.[this.activeSet]?.[partName]?.hidden) return true - return false + return partHidden } /**