├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── test.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc.yml ├── .release-it.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── eslint.config.js ├── lib └── index.d.ts ├── package-lock.json ├── package.json ├── src ├── index.js └── set_defaults.js └── test ├── fixture ├── file1 ├── file2 ├── func └── nested │ └── index ├── index.js └── index.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | 4 | [*.{js,json,yml}] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Automatically normalize line endings for all text-based files 2 | # http://git-scm.com/docs/gitattributes#_end_of_line_conversion 3 | * text=auto eol=lf 4 | 5 | # For binary file types, prevent converting CRLF chars 6 | *.jpg -text 7 | *.png -text -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: ['**'] 5 | pull_request: 6 | branches: ['main'] 7 | 8 | jobs: 9 | pre-test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: actions/setup-node@v3 14 | with: 15 | cache: 'npm' 16 | 17 | - run: npm install 18 | - run: npm run format:check 19 | - run: npm run lint:check 20 | 21 | branch-test: 22 | if: github.ref_name != 'main' && success() 23 | needs: pre-test 24 | runs-on: ${{ matrix.os }} 25 | strategy: 26 | matrix: 27 | os: ['ubuntu-latest', 'windows-latest'] 28 | node: ['14.18.0'] 29 | name: Testing Node ${{ matrix.node }} on ${{ matrix.os }} 30 | steps: 31 | - uses: actions/checkout@v3 32 | - uses: actions/setup-node@v3 33 | with: 34 | cache: 'npm' 35 | 36 | - run: npm install 37 | - run: npm test 38 | 39 | test: 40 | if: github.ref_name == 'main' && success() 41 | needs: pre-test 42 | runs-on: ${{ matrix.os }} 43 | strategy: 44 | matrix: 45 | os: ['ubuntu-latest', 'windows-latest'] 46 | node: ['14.18.0', '16.0', '18.0', '20.0'] 47 | name: Testing Node ${{ matrix.node }} on ${{ matrix.os }} 48 | steps: 49 | - uses: actions/checkout@v3 50 | - uses: actions/setup-node@v3 51 | with: 52 | node-version: ${{ matrix.node }} 53 | cache: 'npm' 54 | 55 | - run: npm install 56 | - run: npm test 57 | - if: matrix.os == 'ubuntu-latest' && matrix.node == '20.0' 58 | run: npm run coverage 59 | - if: matrix.os == 'ubuntu-latest' && matrix.node == '20.0' 60 | uses: coverallsapp/github-action@v2 61 | with: 62 | file: coverage.info 63 | format: lcov 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log* 2 | .npm 3 | *.tgz 4 | .eslintcache 5 | coverage 6 | coverage.info 7 | test/fixtures/*/build 8 | node_modules 9 | lib/*.*js -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock.json = false 2 | sign-git-tag = true 3 | message = Bump package.json to %s -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | test/fixtures/** 2 | .nyc_output/** 3 | package-lock.json -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | trailingComma: none 2 | tabWidth: 2 3 | semi: false 4 | singleQuote: true 5 | bracketSpacing: true 6 | arrowParens: always 7 | printWidth: 120 8 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "before:init": ["npm run lint", "npm test"], 4 | "after:npm:bump": "npm pack", 5 | "after:bump": "auto-changelog -p --commit-limit false --ignore-commit-pattern '^((dev|chore|ci):|Release)'", 6 | "after:release": "echo Successfully released ${name} v${version} to ${repo.repository}." 7 | }, 8 | "git": { 9 | "commitMessage": "Release ${version}", 10 | "commitArgs": ["-S"], 11 | "tagAnnotation": "Release ${version}", 12 | "tagArgs": ["-s"], 13 | "changelog": "auto-changelog -u --commit-limit false --ignore-commit-pattern '^((dev|chore|ci):|Release)' --stdout -t https://raw.githubusercontent.com/release-it/release-it/master/templates/changelog-compact.hbs" 14 | }, 15 | "npm": { 16 | "publish": false 17 | }, 18 | "github": { 19 | "release": true, 20 | "releaseName": "@metalsmith/default-values ${version}", 21 | "tokenRef": "GITHUB_TOKEN", 22 | "assets": ["metalsmith-default-values-${version}.tgz"] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### Changelog 2 | 3 | All notable changes to this project will be documented in this file. Dates are displayed in UTC. 4 | 5 | Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). 6 | 7 | #### [v4.0.1](https://github.com/metalsmith/default-values/compare/v4.0.0...v4.0.1) 8 | 9 | - Updates dlv 3.1.3 -> 3.1.4, fixes security vulns [`d93c72c`](https://github.com/metalsmith/default-values/commit/d93c72cf61946c3df1f35dad8cc74d45a0421e92) 10 | - Documents potential pitfall when setting default object [`46b2db9`](https://github.com/metalsmith/default-values/commit/46b2db92db8c066bc93662b746c882f181a3bc85) 11 | - Changes set_defaults to return change delta, improves debug logs [`88a49da`](https://github.com/metalsmith/default-values/commit/88a49da6ea7e8b8d9d8a8b9e52a380284bb7d6b3) 12 | 13 | ### [v4.0.0](https://github.com/metalsmith/default-values/compare/v3.3.0...v4.0.0) 14 | 15 | > 20 March 2024 16 | 17 | - Provides dual ESM/CJS module and aligns repo setup with other core plugins [`5d23dbd`](https://github.com/metalsmith/default-values/commit/5d23dbd1091e8d09030bdd9b2d6c945e6cd80476) 18 | - test: migrate test coverage from nyc -> c8, tests from CJS to ESM [`e03aacf`](https://github.com/metalsmith/default-values/commit/e03aacf7b4956a962258d791027782fe34953aa2) 19 | - Moves to more permissive LGPL license [`294f77f`](https://github.com/metalsmith/default-values/commit/294f77ffff73f848064dccb2d1817d484e9853c5) 20 | - Provides access to metalsmith.metadata() within default setter functions [`d4a5723`](https://github.com/metalsmith/default-values/commit/d4a57232bb7942ef001ec348a5f44350990f43a2) 21 | - Swaps out lodash.get/set for resp. dlv/dset & fixes security vulnerability [`1c27fd3`](https://github.com/metalsmith/default-values/commit/1c27fd330d854529f7d11b084135fd7ac64d3f36) 22 | - Clarifies default setting at keypaths and buffer conversion [`6c363de`](https://github.com/metalsmith/default-values/commit/6c363de5f777046a03c7432ac425fbf6af089d9f) 23 | - Adds 'strategy' option to allow overwriting existing values [`6e61e96`](https://github.com/metalsmith/default-values/commit/6e61e9653e7bee8d425324805bbc6e88c9fb7a0a) 24 | - Transforms default value into buffer if target type is already a buffer [`7287c08`](https://github.com/metalsmith/default-values/commit/7287c08aa94ef7cd7bdbceb58687acdfdfbc533b) 25 | - Adds generics for file & global metadata to Typescript types [`fb81838`](https://github.com/metalsmith/default-values/commit/fb818381d1246fa023f57f2ee4adaaa53428866f) 26 | - test: adds TS test [`f00280c`](https://github.com/metalsmith/default-values/commit/f00280c89eb01cafd59875e605060a597aae56f0) 27 | - Minor performance enhancement in calling setDefaults [`9dd6a84`](https://github.com/metalsmith/default-values/commit/9dd6a843b8acd413b0d2323f5b056248e74313ae) 28 | - Drops support for Node.js < 14.18.0, updates CI [`fbfa2c7`](https://github.com/metalsmith/default-values/commit/fbfa2c7897368cd272a19d3038d0b98c69a78574) 29 | - Updates dset 3.1.2 -> 3.1.3 [`d9a8d1e`](https://github.com/metalsmith/default-values/commit/d9a8d1e5805f4ccb49c80bec36196d45f87ce7fd) 30 | 31 | #### [v3.3.0](https://github.com/metalsmith/default-values/compare/v3.2.1...v3.3.0) 32 | 33 | > 26 November 2022 34 | 35 | - Allow setting default contents when buffer is empty [`5d41d17`](https://github.com/metalsmith/default-values/commit/5d41d1790b1d4bc6a2905c08d028ab67c83221f4) 36 | - Docs: Add elaborate example with other plugins + single defaults set usage [`bb50253`](https://github.com/metalsmith/default-values/commit/bb502537ffb476208cb032570b3d7c4f4db98847) 37 | - Drops support for Metalsmith < 2.5.0, replaces debug with metalsmith.debug [`b5191b4`](https://github.com/metalsmith/default-values/commit/b5191b471b1d6a3742b57b5eb33629ed32acc12e) 38 | - Adds Typescript support [`3b1b438`](https://github.com/metalsmith/default-values/commit/3b1b438243b0638d0d25db69f55999a97a302f08) 39 | - Document features in README.md [`12102c8`](https://github.com/metalsmith/default-values/commit/12102c81d729d766d7b9be904aa601023b893426) 40 | - Log a warning when pattern matches no files [`c62cbc9`](https://github.com/metalsmith/default-values/commit/c62cbc98f19fa5ac2fe8529996d832895a2cdf93) 41 | - Allows passing a single defaults set as shorthand for [defaultsSet] [`39df8f7`](https://github.com/metalsmith/default-values/commit/39df8f749d22899aaaf4cd1adad5080ae821c193) 42 | - Renames default export initDefaultValues -> defaultValues [`30fc0c5`](https://github.com/metalsmith/default-values/commit/30fc0c5c54392cf21d3ced7eabdab209aac0e26d) 43 | 44 | #### [v3.2.1](https://github.com/metalsmith/default-values/compare/v3.2.0...v3.2.1) 45 | 46 | > 1 August 2022 47 | 48 | - Fixes #24: use ms.match(pattern, files) signature for branch/watch compat [`#24`](https://github.com/metalsmith/default-values/issues/24) 49 | - Updates debug 4.3.3 -> 4.3.4. Upgrades package-lock.json to v2 & fixes security issue [`67a968b`](https://github.com/metalsmith/default-values/commit/67a968b4d3799d6c95d98f2eb2472796c1b59b98) 50 | - Enhancement: add JSDoc example + better options doc [`93b52f2`](https://github.com/metalsmith/default-values/commit/93b52f22b8bf969fa7eb8a7ea10dd8c201c99f91) 51 | 52 | #### [v3.2.0](https://github.com/metalsmith/default-values/compare/v3.1.0...v3.2.0) 53 | 54 | > 2 May 2022 55 | 56 | - Removed console log from code [`#21`](https://github.com/metalsmith/default-values/pull/21) 57 | - Drops Node < 12 support, adds metalsmith as peer & devDep [`7262bf6`](https://github.com/metalsmith/default-values/commit/7262bf604d9d41774bbb13443fd384c3f3d136e1) 58 | - Removes multimatch, uses metalsmith.match. Simplified & refactored [`d6f8407`](https://github.com/metalsmith/default-values/commit/d6f84076e6fe91ca3205c1ed2c14a7cca4694b98) 59 | - Removed Joi options validation in favor of allowing options to be undefined [`19e5f23`](https://github.com/metalsmith/default-values/commit/19e5f23c11cdf2104d6ccce4027ff27728922775) 60 | 61 | #### [v3.1.0](https://github.com/metalsmith/default-values/compare/v3.0.2...v3.1.0) 62 | 63 | > 30 January 2022 64 | 65 | - Updated packages [`#18`](https://github.com/metalsmith/default-values/pull/18) 66 | - Align plugin with other core plugins. Update package.json, generate changelog, fix debug namespace [`cae2058`](https://github.com/metalsmith/default-values/commit/cae2058d73a736adf519f9b83b93b7879ad1a45e) 67 | 68 | #### [v3.0.2](https://github.com/metalsmith/default-values/compare/v3.0.1...v3.0.2) 69 | 70 | > 7 September 2020 71 | 72 | - Updated Packages [`#17`](https://github.com/metalsmith/default-values/pull/17) 73 | - Package updated [`8ae7cf2`](https://github.com/metalsmith/default-values/commit/8ae7cf224db67eb65c239fc473225149f1ab2670) 74 | - Package updated [`0e582de`](https://github.com/metalsmith/default-values/commit/0e582de4de19581ea02792415195395ad646c299) 75 | - Removed outdated badges [`2afcd0e`](https://github.com/metalsmith/default-values/commit/2afcd0e915c27ba7f91538042504160fb47edeb1) 76 | - Removed Node version warning [`f4a6327`](https://github.com/metalsmith/default-values/commit/f4a6327babaa2696062fad1f89d69033b1ee18a4) 77 | 78 | #### [v3.0.1](https://github.com/metalsmith/default-values/compare/v3.0.0...v3.0.1) 79 | 80 | > 7 September 2020 81 | 82 | - Org switch to Metalsmith scope [`#16`](https://github.com/metalsmith/default-values/pull/16) 83 | - Greenkeeper: removed ignore as Node.js v4 support is no longer required [`4b846f5`](https://github.com/metalsmith/default-values/commit/4b846f51e4072aabb2e51271a26742b6a6abde40) 84 | 85 | ### [v3.0.0](https://github.com/metalsmith/default-values/compare/v2.1.0...v3.0.0) 86 | 87 | > 11 September 2018 88 | 89 | - Packages updated [`#15`](https://github.com/metalsmith/default-values/pull/15) 90 | - Update debug to the latest version 🚀 [`#14`](https://github.com/metalsmith/default-values/pull/14) 91 | - Update eslint-config-prettier to the latest version 🚀 [`#12`](https://github.com/metalsmith/default-values/pull/12) 92 | - Travis: Removed testing for v4-5 as they're no longer maintained [`6277b21`](https://github.com/metalsmith/default-values/commit/6277b2161445bb915eef68bfb242f5ebc223596d) 93 | - fix(package): update debug to version 4.0.0 [`7f38589`](https://github.com/metalsmith/default-values/commit/7f38589494cd1d96e1f59f85543f91cf8dcf691a) 94 | - chore(package): update eslint-config-prettier to version 3.0.0 [`ac8689a`](https://github.com/metalsmith/default-values/commit/ac8689ad3d97fe55b43bbef6639c252158a5387d) 95 | - Travis: Added v10 to test [`d3b01e3`](https://github.com/metalsmith/default-values/commit/d3b01e33a35e5d6a7ea78b6dee27f9823cafeb0a) 96 | 97 | #### [v2.1.0](https://github.com/metalsmith/default-values/compare/v2.0.1...v2.1.0) 98 | 99 | > 19 July 2018 100 | 101 | - default value with a function [`#10`](https://github.com/metalsmith/default-values/pull/10) 102 | - Update dependencies to enable Greenkeeper 🌴 [`#11`](https://github.com/metalsmith/default-values/pull/11) 103 | - Revered packages and ignored with greenkeeper [`5e7645c`](https://github.com/metalsmith/default-values/commit/5e7645c6de8d23278f91b6160ec813921d2f16ac) 104 | - Removed BitHound Badges :( [`7b255a9`](https://github.com/metalsmith/default-values/commit/7b255a9e3a861d06744542febfcfa5b31da9f747) 105 | - Added badge [`210c7f9`](https://github.com/metalsmith/default-values/commit/210c7f9dbe4946685f7636ae87505334843fba2b) 106 | - chore(package): update dependencies [`d587d71`](https://github.com/metalsmith/default-values/commit/d587d7103483bd7a5b68d75707e1164f765763d1) 107 | - Update README.md [`e9de8d3`](https://github.com/metalsmith/default-values/commit/e9de8d3493943221012737a2583c760cc81bcca0) 108 | - Badges: updated [`fd0d488`](https://github.com/metalsmith/default-values/commit/fd0d4887a2178ff689ad7333bed9d2872e33de7b) 109 | - Updated package scripts [`4c26c3d`](https://github.com/metalsmith/default-values/commit/4c26c3d0110cc2ed99700ae7d7e30cb83dfd7b8b) 110 | - docs(readme): add Greenkeeper badge [`489007a`](https://github.com/metalsmith/default-values/commit/489007ae174d55f6ef375a0fe5984461286e3ca0) 111 | - Update README.md [`9d82281`](https://github.com/metalsmith/default-values/commit/9d82281a72a774c08c0c53f8d4ed4a4c8957e1f8) 112 | 113 | #### [v2.0.1](https://github.com/metalsmith/default-values/compare/v2.0.0...v2.0.1) 114 | 115 | > 24 January 2018 116 | 117 | - Dependencies: Updated Fixes #4 and general maintenance [`#8`](https://github.com/metalsmith/default-values/pull/8) 118 | - Merge pull request #8 from woodyrew/fix-dependencies [`#4`](https://github.com/metalsmith/default-values/issues/4) 119 | - Dependencies: Updated Fixes #4 and general maintenance [`#4`](https://github.com/metalsmith/default-values/issues/4) [`#7`](https://github.com/metalsmith/default-values/issues/7) [`#6`](https://github.com/metalsmith/default-values/issues/6) [`#3`](https://github.com/metalsmith/default-values/issues/3) [`#5`](https://github.com/metalsmith/default-values/issues/5) 120 | - Extra information and examples [`ae84ed0`](https://github.com/metalsmith/default-values/commit/ae84ed0d8d926695efb890150781489f7c3e1352) 121 | - All the badges [`df8316f`](https://github.com/metalsmith/default-values/commit/df8316f62a4658110eb8596f30f3635d7a4bc9de) 122 | - README: Formatting [`cfa537f`](https://github.com/metalsmith/default-values/commit/cfa537fa3f93ac2b8e4c65c7aeec95c72a0deb98) 123 | - Updated Dependencies [`412293b`](https://github.com/metalsmith/default-values/commit/412293be44b6c9d0beee54b590e02ea64c61a2ec) 124 | - Dependencies: Downgraded Joi to 12 for Node <6 compatibility [`4df7f3c`](https://github.com/metalsmith/default-values/commit/4df7f3c70b75e09f876006dbae97582eb49f7d4d) 125 | 126 | ### [v2.0.0](https://github.com/metalsmith/default-values/compare/v1.0.1...v2.0.0) 127 | 128 | > 7 August 2017 129 | 130 | - Dependancies: Upgraded [`#1`](https://github.com/metalsmith/default-values/pull/1) 131 | - Require typo: Corrected; Fixes #2 [`#2`](https://github.com/metalsmith/default-values/issues/2) 132 | - npm 5 - Added package-lock.json [`c3be173`](https://github.com/metalsmith/default-values/commit/c3be173fe482521ad7cd8bbaebb3249a02df0db5) 133 | - ES6 - Converted to modern JS; Also added allownUnknown for config [`4e9373a`](https://github.com/metalsmith/default-values/commit/4e9373a6f0a64b96a2f4077da6a9e473abc822b9) 134 | - Updated dev dependancies and switched to TAP [`2abbc83`](https://github.com/metalsmith/default-values/commit/2abbc8386f5e79a0c963b3b49de5f763d1b03360) 135 | - Removed unecessary test index file [`914e71e`](https://github.com/metalsmith/default-values/commit/914e71e068ce535cc18a7d09a135a02820ab6184) 136 | - Added engines and updated travis node versions [`616c5f7`](https://github.com/metalsmith/default-values/commit/616c5f769e86a02582a7bb7aca5970fd8dc59c6d) 137 | - Dependencies updated [`b6574a2`](https://github.com/metalsmith/default-values/commit/b6574a2770da152cfbeea66bc1e39dc859128fd2) 138 | 139 | #### [v1.0.1](https://github.com/metalsmith/default-values/compare/v1.0.0...v1.0.1) 140 | 141 | > 27 August 2016 142 | 143 | - Dependancies: Upgraded [`498e2d4`](https://github.com/metalsmith/default-values/commit/498e2d488bda5ca69906e7e804ad0bb174bc6752) 144 | - Travis: Limited to NodeJS v4 and above. [`c724323`](https://github.com/metalsmith/default-values/commit/c72432396611a6e9c9d812471e825ca96f0b4a42) 145 | - Travis: Removed 4.0 [`c58c6b7`](https://github.com/metalsmith/default-values/commit/c58c6b7f760fd544c52c88b6c49386e7eb430632) 146 | 147 | #### v1.0.0 148 | 149 | > 27 August 2016 150 | 151 | - Initial commit [`5238b14`](https://github.com/metalsmith/default-values/commit/5238b1401b34e384592239730e55f9a14367b313) 152 | - Plugin inital state: open sourced for <3 [`18844b6`](https://github.com/metalsmith/default-values/commit/18844b615993777b5183a63cc9321c4f4e38adf1) 153 | - Testing: Added basic tests and updated readme. [`d290ca5`](https://github.com/metalsmith/default-values/commit/d290ca5bd892d92cd58bd1e43d66aed7f923760e) 154 | - Error: Returns error properly [`b72f12a`](https://github.com/metalsmith/default-values/commit/b72f12a7c2569c25b50970791392746cba4eea22) 155 | - Travis: Added [`2fa32ab`](https://github.com/metalsmith/default-values/commit/2fa32ab1d40903ac23ea5a198b8a7ae4c4160c4c) 156 | - NPM & Travis Icons: added [`6f0e103`](https://github.com/metalsmith/default-values/commit/6f0e1034743b02b5346ddc37a16add84ff208625) 157 | - Added dependencies badge [`cea37af`](https://github.com/metalsmith/default-values/commit/cea37af6d04265ded7b01168faa957972e6ee0f3) 158 | - Readme: fix require statement [`d4225f2`](https://github.com/metalsmith/default-values/commit/d4225f2a5533d4d7a567935a1529e7b4bac60f57) 159 | - ESLint: update to default - duplicate key [`8668a5a`](https://github.com/metalsmith/default-values/commit/8668a5aaa14eff117d738d9150906abcc43de5a2) 160 | - ESLint Verson: Set [`d685194`](https://github.com/metalsmith/default-values/commit/d6851941ab931fc870f4192274b280c630ae0265) 161 | - Legacy compatibility: Switched to Joi 6.x [`7c1f1ea`](https://github.com/metalsmith/default-values/commit/7c1f1ea675aff4ffc4402300c9e6582fe5430166) 162 | - Update defaults to remove duplicate [`429b664`](https://github.com/metalsmith/default-values/commit/429b664e4d98805e811f9d776252b2c3a88c9a5f) 163 | - Travis: Renamed to correct name [`88b0688`](https://github.com/metalsmith/default-values/commit/88b0688a6745255efe5470287106d7ed2a5ecba1) 164 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @metalsmith/default-values 2 | 3 | A Metalsmith plugin for setting default values to file metadata. 4 | 5 | [![metalsmith: core plugin][metalsmith-badge]][metalsmith-url] 6 | [![npm: version][npm-badge]][npm-url] 7 | [![ci: build][ci-badge]][ci-url] 8 | [![code coverage][codecov-badge]][codecov-url] 9 | [![license: LGPL-3.0][license-badge]][license-url] 10 | 11 | ## Features 12 | 13 | - sets default values for metadata keys and file contents on files matched by pattern 14 | - does not overwrite or transform key values that are already defined, unless `strategy: 'overwrite'`. 15 | - can set computed defaults based on other file keys or metalsmith metadata 16 | 17 | ## Installation 18 | 19 | NPM: 20 | 21 | ```bash 22 | npm install @metalsmith/default-values 23 | ``` 24 | 25 | Yarn: 26 | 27 | ```bash 28 | yarn add @metalsmith/default-values 29 | ``` 30 | 31 | ## Usage: 32 | 33 | Pass `@metalsmith/default-values` to `metalsmith.use`: 34 | 35 | ```js 36 | import defaultValues from '@metalsmith/default-values' 37 | 38 | // single defaults set for all HTML and markdown files 39 | metalsmith.use({ 40 | defaults: { 41 | pattern: '**/*.md', 42 | title: 'Lorem ipsum' 43 | } 44 | }) 45 | 46 | metalsmith.use( 47 | defaultValues([ 48 | { 49 | pattern: 'posts/*.md', 50 | defaults: { 51 | layout: 'post.hbs', 52 | date: function (post) { 53 | return post.stats.ctime 54 | } 55 | } 56 | }, 57 | { 58 | pattern: 'diary/*.md', 59 | defaults: { 60 | layout: 'diary.hbs', 61 | private: true 62 | } 63 | }, 64 | { 65 | pattern: ['diary/*.md', 'archive/**/*.md'], 66 | defaults: { 67 | no_index: true 68 | } 69 | }, 70 | { 71 | pattern: '**/*.md', 72 | defaults: { 73 | layout: 'default.hbs' 74 | } 75 | } 76 | ]) 77 | ) 78 | ``` 79 | 80 | ### Options 81 | 82 | `@metalsmith/default-values` takes an array of defaults sets or a single defaults set. The defaults set has the following properties: 83 | 84 | - `pattern` (`string|string[]`): One or more glob patterns to match file paths. Defaults to `'**'` (all). 85 | - `defaults` (`Object`): An object whose key-value pairs will be added to file metadata. You can also specify a function `callback(file, metadata)` to set dynamic defaults based on existing file or global metadata. 86 | - `strategy` (`'keep'|'overwrite'`): Strategy to handle setting defaults to keys that are aleady defined. 87 | 88 | ### Examples 89 | 90 | #### Setting defaults at a keypath 91 | 92 | You can set a default at a file's nested keypath: 93 | 94 | ```js 95 | metalsmith.use( 96 | defaultValues({ 97 | pattern: '**/*.md', 98 | defaults: { 99 | pubdate(file) { return new Date() } 100 | 'config.scripts.app': '/app.js' 101 | } 102 | }) 103 | ) 104 | ``` 105 | 106 | #### Setting default objects 107 | 108 | If you assign a default object like this: 109 | 110 | ```js 111 | metalsmith.use(defaultValues({ defaults: { someObject: { id: 'some' } } })) 112 | ``` 113 | 114 | All files to which the value was set will refer to the same object (`files['index.html'].someObject === files['other.html'].someObject`). If the object needs to be unique for each file, use a default setter function or specify each property as a keypath: 115 | 116 | ```js 117 | // using a function 118 | metalsmith.use( 119 | defaultValues({ 120 | defaults: { 121 | someObject: () => ({ id: 'some', other: true }) 122 | } 123 | }) 124 | ) 125 | 126 | // using keypaths 127 | metalsmith.use( 128 | defaultValues({ 129 | defaults: { 130 | 'someObject.id': 'some', 131 | 'someObject.other': true 132 | } 133 | }) 134 | ) 135 | ``` 136 | 137 | #### Setting default contents 138 | 139 | You can set a file's default contents (which is a Node buffer) and any other Buffer properties: 140 | 141 | ```js 142 | metalsmith.use( 143 | defaultValues({ 144 | pattern: '**/*.md', 145 | defaults: { 146 | strategy: 'overwrite', 147 | contents: Buffer.from('TO DO') 148 | } 149 | }) 150 | ) 151 | ``` 152 | 153 | When using a JSON config, a string can be used as default and it will automatically be transformed into a buffer. 154 | 155 | #### Setting dynamic defaults 156 | 157 | You can set dynamic defaults based on current file metadata or metalsmith metadata: 158 | 159 | ```js 160 | metalsmith 161 | .metadata({ 162 | build: { timestamp: Date.now() } 163 | }) 164 | .use( 165 | defaultValues([ 166 | { 167 | strategy: 'overwrite', 168 | defaults: { 169 | buildInfo(file, metadata) { 170 | return metadata.build 171 | }, 172 | excerpt(file) { 173 | return file.contents.toString().slice(0, 200) 174 | } 175 | } 176 | } 177 | ]) 178 | ) 179 | ``` 180 | 181 | #### Combining with other plugins 182 | 183 | @metalsmith/default-values works great with other `@metalsmith` plugins. The example below attaches a collection and layout matching the parent directory for all files in the directories `services`,`products`, and `articles`: 184 | 185 | ```js 186 | import slugify from 'slugify' 187 | const contentTypes = ['product', 'service', 'article'] 188 | 189 | metalsmith 190 | .use( 191 | defaultValues( 192 | contentTypes.map((contentType) => ({ 193 | pattern: `${contentType}s/*.md`, // pluralized 194 | defaults: { 195 | collection: `${contentType}s`, // pluralized 196 | bodyClass: contentType, 197 | layout: `${contentType}.njk`, // using jstransformer-nunjucks 198 | contentLength(file) { 199 | if (file.contents) return file.contents.toString().length 200 | return 0 201 | } 202 | } 203 | })) 204 | ) 205 | ) 206 | .use(markdown()) // @metalsmith/markdown 207 | .use(collections()) // @metalsmith/collections 208 | .use( 209 | layouts({ 210 | // @metalsmith/layouts 211 | pattern: '**/*.html' 212 | }) 213 | ) 214 | ``` 215 | 216 | ### Debug 217 | 218 | To enable debug logs, set the `DEBUG` environment variable to `@metalsmith/default-values*`: 219 | 220 | ```js 221 | metalsmith.env('DEBUG', '@metalsmith/default-values*') 222 | ``` 223 | 224 | Alternatively you can set `DEBUG` to `@metalsmith/*` to debug all Metalsmith core plugins. 225 | 226 | ### CLI usage 227 | 228 | To use this plugin with the Metalsmith CLI, add `@metalsmith/default-values` to the `plugins` key in your `metalsmith.json` file: 229 | 230 | ```json 231 | { 232 | "plugins": [ 233 | { 234 | "@metalsmith/default-values": [ 235 | { 236 | "pattern": "diary/*.md", 237 | "defaults": { 238 | "layout": "diary.hbs", 239 | "private": true 240 | } 241 | } 242 | ] 243 | } 244 | ] 245 | } 246 | ``` 247 | 248 | ## License 249 | 250 | [LGPL-3.0 or later](LICENSE) 251 | 252 | [npm-badge]: https://img.shields.io/npm/v/@metalsmith/default-values.svg 253 | [npm-url]: https://www.npmjs.com/package/@metalsmith/default-values 254 | [ci-badge]: https://github.com/metalsmith/default-values/actions/workflows/test.yml/badge.svg 255 | [ci-url]: https://github.com/metalsmith/default-values/actions/workflows/test.yml 256 | [metalsmith-badge]: https://img.shields.io/badge/metalsmith-core_plugin-green.svg?longCache=true 257 | [metalsmith-url]: https://metalsmith.io 258 | [codecov-badge]: https://img.shields.io/coveralls/github/metalsmith/default-values 259 | [codecov-url]: https://coveralls.io/github/metalsmith/default-values 260 | [license-badge]: https://img.shields.io/github/license/metalsmith/default-values 261 | [license-url]: LICENSE 262 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import prettier from 'eslint-config-prettier' 4 | import pluginImport from 'eslint-plugin-import' 5 | import pluginNode from 'eslint-plugin-n' 6 | 7 | const config = [ 8 | { 9 | ignores: ['lib', 'test/fixtures'], 10 | languageOptions: { 11 | globals: { 12 | ...globals.node 13 | }, 14 | 15 | ecmaVersion: 2020, 16 | sourceType: 'module' 17 | }, 18 | 19 | rules: { 20 | ...js.configs.recommended.rules, 21 | 'no-console': 'error', 22 | 'prefer-const': 'error', 23 | 'no-var': 'error', 24 | 'no-use-before-define': 'error', 25 | 'no-unused-vars': [ 26 | 'error', 27 | { 28 | argsIgnorePattern: '^_', 29 | varsIgnorePattern: '^_', 30 | caughtErrorsIgnorePattern: '^_' 31 | } 32 | ], 33 | 'no-await-in-loop': 'error', 34 | 'n/exports-style': [0, 'error'], 35 | 'import/first': 'error', 36 | 'import/no-anonymous-default-export': 'error', 37 | 'import/no-unassigned-import': 'error', 38 | 'import/no-internal-modules': [ 39 | 'error', 40 | { 41 | allow: ['src/**'] 42 | } 43 | ] 44 | }, 45 | plugins: { 46 | import: pluginImport, 47 | n: pluginNode, 48 | prettier: prettier 49 | } 50 | }, 51 | { 52 | files: ['test/{**/,}*.js'], 53 | languageOptions: { 54 | globals: { ...globals.mocha } 55 | } 56 | } 57 | ] 58 | 59 | export default config 60 | -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | import { Plugin, File } from 'metalsmith'; 2 | 3 | export default defaultValues; 4 | 5 | export type DefaultSetter = (data:FileMeta, metadata: GlobalMeta) => any 6 | export interface DefaultsSet { 7 | /** an object whose keys will be set as file metadata keys */ 8 | defaults: { 9 | [key:string]: DefaultSetter|string|boolean|number|Object; 10 | } 11 | /** 12 | * 1 or more glob patterns to match files. 13 | * @default '**' 14 | **/ 15 | pattern?: string; 16 | /** 17 | * Strategy to handle setting defaults to keys that are aleady defined. 18 | * @default 'keep' 19 | */ 20 | strategy?: 'keep'|'overwrite' 21 | } 22 | export type Options = DefaultsSet|DefaultsSet[] 23 | /** 24 | * Set `defaults` to file metadata matching `pattern`'s. 25 | * 26 | * @example 27 | * metalsmith.use(defaultValues({ 28 | pattern: 'posts/*.md', 29 | defaults: { 30 | layout: 'post.hbs', 31 | draft: false, 32 | date(post) { 33 | return post.stats.ctime 34 | } 35 | } 36 | })) 37 | **/ 38 | declare function defaultValues(options: Options): Plugin; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@metalsmith/default-values", 3 | "version": "4.0.1", 4 | "description": "A metalsmith plugin for setting default values to file metadata.", 5 | "keywords": [ 6 | "default-values", 7 | "metalsmith-plugin", 8 | "metalsmith", 9 | "static-site" 10 | ], 11 | "homepage": "https://github.com/metalsmith/default-values#readme", 12 | "bugs": { 13 | "url": "https://github.com/metalsmith/default-values/issues" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/metalsmith/default-values.git" 18 | }, 19 | "license": "LGPL-3.0-or-later", 20 | "author": "Woody Goodricke ", 21 | "maintainers": [ 22 | "Kevin Van Lierde (https://webketje.com)" 23 | ], 24 | "source": "src/index.js", 25 | "main": "lib/index.cjs", 26 | "module": "lib/index.js", 27 | "exports": { 28 | "import": "./lib/index.js", 29 | "require": "./lib/index.cjs" 30 | }, 31 | "type": "module", 32 | "types": "lib/index.d.ts", 33 | "directories": { 34 | "lib": "lib", 35 | "test": "test" 36 | }, 37 | "files": [ 38 | "lib", 39 | "CHANGELOG.md" 40 | ], 41 | "scripts": { 42 | "changelog": "auto-changelog -u --commit-limit false --ignore-commit-pattern '^((dev|chore|test|ci):|Release)'", 43 | "format": "prettier --write \"**/*.{yml,md,js,json}\"", 44 | "format:check": "prettier --list-different \"**/*.{yml,md,js,json}\"", 45 | "lint": "eslint --fix .", 46 | "lint:check": "eslint --fix-dry-run .", 47 | "coverage": "npm test && c8 report --reporter=text-lcov > ./coverage.info", 48 | "release": "npm run build && release-it .", 49 | "build": "microbundle --target node --no-sourcemap -f cjs,esm --generateTypes=false", 50 | "test": "c8 mocha", 51 | "pretest": "npm run build" 52 | }, 53 | "dependencies": { 54 | "dlv": "^1.1.3", 55 | "dset": "^3.1.4" 56 | }, 57 | "devDependencies": { 58 | "auto-changelog": "^2.5.0", 59 | "c8": "^10.1.3", 60 | "eslint": "^9.17.0", 61 | "eslint-config-prettier": "^9.1.0", 62 | "eslint-plugin-import": "^2.31.0", 63 | "eslint-plugin-n": "^17.15.0", 64 | "metalsmith": "^2.6.3", 65 | "microbundle": "^0.15.1", 66 | "mocha": "^11.0.1", 67 | "prettier": "^3.4.2", 68 | "release-it": "^17.10.0" 69 | }, 70 | "peerDependencies": { 71 | "metalsmith": "^2.5.0" 72 | }, 73 | "engines": { 74 | "node": ">=14.18.0" 75 | }, 76 | "publishConfig": { 77 | "access": "public" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import getDefaultsSetter from './set_defaults.js' 2 | 3 | /** 4 | * @callback DefaultSetter 5 | * @param {import('metalsmith').File} file 6 | * @param {Object} metadata 7 | */ 8 | 9 | /** 10 | * @typedef {Object} DefaultsSet 11 | * @property {string|string[]} [pattern="**"] 1 or more glob patterns to match files. Defaults to `'**'` (all). 12 | * @property {Object} [defaults={}] an object whose keys will be set as file metadata keys 13 | * @property {'keep'|'overwrite'} [strategy="keep"] Strategy to handle setting defaults to keys that are aleady defined. Defaults to `'keep'` 14 | */ 15 | 16 | /** @type {DefaultsSet} */ 17 | const defaultDefaultsSet = { 18 | defaults: {}, 19 | strategy: 'keep', 20 | pattern: '**' 21 | } 22 | 23 | /** 24 | * Set `defaults` to file metadata matching `pattern`'s. 25 | * 26 | * @param {DefaultsSet|DefaultsSet[]} options an array of defaults sets to add to files matched by pattern 27 | * @return {import('metalsmith').Plugin} 28 | * 29 | * @example 30 | * metalsmith.use(defaultValues({ 31 | pattern: 'posts/*.md', 32 | defaults: { 33 | layout: 'post.hbs', 34 | draft: false, 35 | date(post) { 36 | return post.stats.ctime 37 | } 38 | } 39 | })) 40 | **/ 41 | 42 | function defaultValues(options) { 43 | return function defaultValues(files, metalsmith, done) { 44 | const debug = metalsmith.debug('@metalsmith/default-values') 45 | if (!Array.isArray(options) && typeof options === 'object' && options !== null) { 46 | options = [options] 47 | } 48 | debug('Running with options: %O ', options) 49 | const defaultSets = (options || []).map((defaultsSet) => Object.assign({}, defaultDefaultsSet, defaultsSet)) 50 | 51 | // Loop through configurations 52 | defaultSets.forEach(function ({ pattern, defaults, strategy }) { 53 | const matches = metalsmith.match(pattern, Object.keys(files)) 54 | const defaultsEntries = Object.entries(defaults) 55 | debug.info('Matched %s files to pattern "%s": %o', matches.length, pattern, matches) 56 | if (matches.length) { 57 | const setDefaults = getDefaultsSetter(defaultsEntries, strategy) 58 | matches.forEach((file) => { 59 | const changed = setDefaults(files[file], metalsmith.metadata()) 60 | debug.info('Values set for file "%s": %O', file, changed) 61 | }) 62 | } else { 63 | debug.warn('No matches for pattern "%s"', pattern) 64 | } 65 | }) 66 | 67 | done() 68 | } 69 | } 70 | 71 | export default defaultValues 72 | -------------------------------------------------------------------------------- /src/set_defaults.js: -------------------------------------------------------------------------------- 1 | import { Buffer } from 'buffer' 2 | import get from 'dlv' 3 | import { dset as set } from 'dset' 4 | 5 | /** 6 | * Sets defaults for object values 7 | * @param {Array>} defaults 8 | * @param {'keep'|'overwrite'} strategy 9 | * @return {import('.').DefaultSetter} Takes an object and sets defaults 10 | */ 11 | function set_defaults(defaults, strategy) { 12 | return (item, context) => { 13 | const delta = {} 14 | defaults.forEach(([key, defaultValue]) => { 15 | const value = get(item, key) 16 | if ( 17 | strategy === 'overwrite' || 18 | value === void 0 || 19 | value === null || 20 | (Buffer.isBuffer(value) && value.toString().trim().length === 0) 21 | ) { 22 | if (typeof defaultValue === 'function') defaultValue = defaultValue(item, context) 23 | if (Buffer.isBuffer(value) && !Buffer.isBuffer(defaultValue)) defaultValue = Buffer.from(defaultValue) 24 | set(item, key, defaultValue) 25 | set(delta, key, defaultValue) 26 | } 27 | }) 28 | return delta 29 | } 30 | } 31 | 32 | export default set_defaults 33 | -------------------------------------------------------------------------------- /test/fixture/file1: -------------------------------------------------------------------------------- 1 | --- 2 | existing_key: yes 3 | --- 4 | Hello world -------------------------------------------------------------------------------- /test/fixture/file2: -------------------------------------------------------------------------------- 1 | --- 2 | existing_key: yes 3 | default_val: false 4 | --- -------------------------------------------------------------------------------- /test/fixture/func: -------------------------------------------------------------------------------- 1 | --- 2 | funcDefault: 1 3 | --- -------------------------------------------------------------------------------- /test/fixture/nested/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metalsmith/default-values/8f138492ee59238e78979f2a6fbd6efa02857b3a/test/fixture/nested/index -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | import { fileURLToPath } from 'node:url' 3 | import { readFileSync } from 'node:fs' 4 | import assert from 'node:assert' 5 | import path from 'node:path' 6 | 7 | import Metalsmith from 'metalsmith' 8 | import plugin from '../src/index.js' 9 | 10 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 11 | const { name } = JSON.parse(readFileSync(path.resolve(__dirname, '../package.json'))) 12 | 13 | let set_defaults_lib 14 | 15 | function relevantProps(expected, files) { 16 | return Object.keys(expected).reduce((acc, filename) => { 17 | acc[filename] = {} 18 | Object.keys(expected[filename]).forEach((prop) => { 19 | acc[filename][prop] = files[filename] && files[filename][prop] 20 | if (acc[filename][prop] instanceof Buffer) acc[filename][prop] = acc[filename][prop].toString() 21 | }) 22 | return acc 23 | }, {}) 24 | } 25 | 26 | describe('@metalsmith/default-values', function () { 27 | before(function (done) { 28 | import('../src/set_defaults.js').then((imported) => { 29 | set_defaults_lib = imported.default 30 | done() 31 | }) 32 | }) 33 | 34 | it('should export a named plugin function matching package.json name', function () { 35 | const namechars = name.split('/')[1] 36 | const camelCased = namechars.split('').reduce((str, char, i) => { 37 | str += namechars[i - 1] === '-' ? char.toUpperCase() : char === '-' ? '' : char 38 | return str 39 | }, '') 40 | assert.strictEqual(plugin().name, camelCased) 41 | }) 42 | 43 | it('should not crash the metalsmith build when using default options', function (done) { 44 | Metalsmith(__dirname) 45 | .source('.') 46 | .env('DEBUG', process.env.DEBUG) 47 | .use(plugin()) 48 | .process((err) => { 49 | if (err) done(err) 50 | else done() 51 | }) 52 | }) 53 | it('sets a key when not present', function (done) { 54 | const ms = Metalsmith(__dirname) 55 | .source('./fixture') 56 | .env('DEBUG', process.env.DEBUG) 57 | .use( 58 | plugin([ 59 | { 60 | defaults: { default_val: true } 61 | }, 62 | { 63 | pattern: 'file2', 64 | defaults: { another_default: 'hello' } 65 | } 66 | ]) 67 | ) 68 | const expected = { 69 | file1: { 70 | existing_key: 'yes', 71 | default_val: true 72 | }, 73 | file2: { 74 | existing_key: 'yes', 75 | default_val: false, 76 | another_default: 'hello' 77 | } 78 | } 79 | 80 | ms.process((err, files) => { 81 | assert.ifError(err, 'Has not errored') 82 | assert.deepStrictEqual(relevantProps(expected, files), expected) 83 | done() 84 | }) 85 | }) 86 | 87 | it('logs a warning when no files match a pattern', function (done) { 88 | let warning 89 | function Debugger() {} 90 | Debugger.warn = (msg) => { 91 | warning = msg 92 | } 93 | Debugger.info = () => {} 94 | 95 | const msStub = { 96 | match() { 97 | return [] 98 | }, 99 | debug() { 100 | return Debugger 101 | } 102 | } 103 | 104 | plugin([ 105 | { 106 | pattern: 'non-existant', 107 | defaults: { wontbeset: 1 } 108 | } 109 | ])({}, msStub, () => { 110 | try { 111 | assert.strictEqual(warning, 'No matches for pattern "%s"') 112 | } catch (err) { 113 | done(err) 114 | } 115 | done() 116 | }) 117 | }) 118 | 119 | it('supports defining a function to return default value', function (done) { 120 | const ms = Metalsmith(__dirname) 121 | .source('./fixture') 122 | .env('DEBUG', process.env.DEBUG) 123 | .use( 124 | plugin([ 125 | { 126 | pattern: 'func', 127 | defaults: { 128 | computedDefault({ funcDefault }) { 129 | return funcDefault + 100 130 | } 131 | } 132 | } 133 | ]) 134 | ) 135 | const expected = { 136 | func: { 137 | funcDefault: 1, 138 | computedDefault: 101 139 | } 140 | } 141 | 142 | ms.process((err, files) => { 143 | if (err) done(err) 144 | try { 145 | assert.deepStrictEqual(relevantProps(expected, { func: files.func }), expected) 146 | done() 147 | } catch (err) { 148 | done(err) 149 | } 150 | }) 151 | }) 152 | 153 | it('allows passing a single defaults set as shorthand', function (done) { 154 | const ms = Metalsmith(__dirname) 155 | .source('./fixture') 156 | .env('DEBUG', process.env.DEBUG) 157 | .use( 158 | plugin({ 159 | defaults: { all: 'ok' } 160 | }) 161 | ) 162 | const expected = { 163 | file1: { all: 'ok' }, 164 | file2: { all: 'ok' }, 165 | func: { all: 'ok' } 166 | } 167 | 168 | expected[path.join('nested', 'index')] = { all: 'ok' } 169 | 170 | ms.process((err, files) => { 171 | if (err) done(err) 172 | try { 173 | assert.deepStrictEqual(relevantProps(expected, files), expected) 174 | done() 175 | } catch (err) { 176 | done(err) 177 | } 178 | }) 179 | }) 180 | 181 | it('allows setting default contents when empty buffer', function (done) { 182 | const ms = Metalsmith(__dirname) 183 | .source('./fixture') 184 | .env('DEBUG', process.env.DEBUG) 185 | .use( 186 | plugin({ 187 | pattern: 'file*', 188 | defaults: { contents: Buffer.from('Lorem ipsum') } 189 | }) 190 | ) 191 | 192 | ms.process((err, files) => { 193 | if (err) done(err) 194 | try { 195 | const expected = { 196 | file1: { contents: 'Hello world' }, 197 | file2: { contents: 'Lorem ipsum' } 198 | } 199 | assert.deepStrictEqual(relevantProps(expected, files), expected) 200 | done() 201 | } catch (err) { 202 | done(err) 203 | } 204 | }) 205 | }) 206 | 207 | // this is essential for CLI flows to be able to set default values in JSON 208 | it('converts non-buffer defaults to buffers if the target key is already a buffer', function (done) { 209 | const ms = Metalsmith(__dirname) 210 | .source('./fixture') 211 | .env('DEBUG', process.env.DEBUG) 212 | .use( 213 | plugin({ 214 | pattern: 'file2', 215 | defaults: { contents: 'Lorem ipsum' } 216 | }) 217 | ) 218 | 219 | ms.process((err, files) => { 220 | if (err) done(err) 221 | try { 222 | assert(Buffer.isBuffer(files.file2.contents)) 223 | assert.strictEqual(files.file2.contents.toString(), 'Lorem ipsum') 224 | done() 225 | } catch (err) { 226 | done(err) 227 | } 228 | }) 229 | }) 230 | 231 | describe('set_defaults', function () { 232 | it('initialisation returns a function', function () { 233 | const actual = set_defaults_lib() 234 | 235 | assert.strictEqual(typeof actual, 'function', 'Function returned after initialisation') 236 | }) 237 | 238 | it('sets a key when not present', function () { 239 | const defaults = { 240 | default_val: true 241 | } 242 | const set_defaults = set_defaults_lib(Object.entries(defaults)) 243 | assert.strictEqual(typeof set_defaults, 'function', 'Function returned after initialisation') 244 | const actual = set_defaults({ 245 | initial: 'yes' 246 | }) 247 | const expected = { 248 | default_val: true 249 | } 250 | 251 | assert.deepStrictEqual(actual, expected) 252 | }) 253 | 254 | it('overwrites a key when strategy is "overwrite"', function () { 255 | const defaults = { 256 | initial: 'no', 257 | default_val: true 258 | } 259 | const set_defaults = set_defaults_lib(Object.entries(defaults), 'overwrite') 260 | assert.strictEqual(typeof set_defaults, 'function', 'Function returned after initialisation') 261 | const actual = set_defaults({ 262 | initial: 'yes' 263 | }) 264 | const expected = { 265 | initial: 'no', 266 | default_val: true 267 | } 268 | 269 | assert.deepStrictEqual(actual, expected) 270 | }) 271 | 272 | it('sets a default computed from additional metadata and returns the delta', function () { 273 | const defaults = { 274 | default_val: true, 275 | buildVersion(file, globalMeta) { 276 | return globalMeta.buildVersion 277 | } 278 | } 279 | const set_defaults = set_defaults_lib(Object.entries(defaults)) 280 | const actual = set_defaults({ initial: 'yes' }, { buildVersion: '1.0.0' }) 281 | const expected = { 282 | default_val: true, 283 | buildVersion: '1.0.0' 284 | } 285 | 286 | assert.deepStrictEqual(actual, expected) 287 | }) 288 | }) 289 | 290 | // this is essential for CLI flows to be able to set default values in JSON 291 | it('sets a default at a key path ', function () { 292 | const defaults = { 293 | 'some.nested.def.value': true 294 | } 295 | const set_defaults = set_defaults_lib(Object.entries(defaults)) 296 | const actual = set_defaults({ initial: 'yes' }) 297 | const expected = { 298 | some: { 299 | nested: { 300 | def: { 301 | value: true 302 | } 303 | } 304 | } 305 | } 306 | 307 | assert.deepStrictEqual(actual, expected) 308 | }) 309 | }) 310 | -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | import Metalsmith from 'metalsmith' 2 | import defaultValues from '../lib' 3 | 4 | type File = Metalsmith.File<{ 5 | layout?: string 6 | draft?: boolean 7 | }> 8 | 9 | defaultValues({ 10 | strategy: 'overwrite', 11 | pattern: '**/*.html', 12 | defaults: { 13 | excerpt(file) { 14 | return file.contents.toString().slice(0, 250) + '...' 15 | }, 16 | buildVersion(_, meta) { 17 | return meta.buildVersion 18 | }, 19 | draft(file) { 20 | return file.draft 21 | }, 22 | layout: 'default.njk' 23 | } 24 | }) --------------------------------------------------------------------------------