mdat 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +4 -0
- package/dist/api.d.ts +22 -0
- package/dist/config.d.ts +31 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/mdat-json-loader.d.ts +2 -0
- package/dist/utilities.d.ts +11 -0
- package/license.txt +21 -0
- package/package.json +81 -0
- package/readme.md +354 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{defaultLoaders as S}from"cosmiconfig";function b(e,o){let n=S[".json"],d=n(e,o);return O(d)}function O(e,o="",n={}){for(let[d,r]of Object.entries(e)){let t=o?`${o}.${d}`:d;typeof r=="object"&&r!==null&&!Array.isArray(r)?O(r,t,n):r===null?n[t]="null":n[t]=r.toString()}return n}import C from"chalk";import{cosmiconfig as R}from"cosmiconfig";import I from"node:fs/promises";import J from"node:path";import L from"plur";import{deepMergeDefined as w,log as m,optionsSchema as V,rulesSchema as N}from"remark-mdat";async function v(e){let{additionalConfig:o,additionalRules:n,configExtensionSchema:d,searchFrom:r}=e??{},t={},a=V;d!==void 0&&(a=a.merge(d));let f=R("mdat"),p=await f.search(r);if(p){let{config:u,filepath:s}=p;m.info(`Using config from "${s}"`);let i=k(u,a);i&&(t=w(t,i))}if(o!==void 0){let u=Array.isArray(o)?o:[o];for(let s of u){let i;if(typeof s=="string"){let c=await f.load(s);if(c==null)continue;let{config:l,filepath:x}=c;m.info(`Loaded additional config from "${x}"`),i=l}else i=s;if(i===void 0)continue;m.info("Merging configuration object");let g=k(i,a);g!==void 0&&(t=w(t,g))}}if(n!==void 0){let u=Array.isArray(n)?n:[n],s=R("mdat",{loaders:{".json":b}});for(let i of u){let g;if(typeof i=="string"){let l;if(J.basename(i).endsWith("package.json")){let F=await I.readFile(i,"utf8");l={config:b(i,F),filepath:i}}else l=await s.load(i);if(l==null)continue;let{config:x,filepath:A}=l;m.info(`Loaded additional config from "${A}"`),g=x}else g=i;if(g===void 0)continue;m.info("Merging rules into configuration object");let c=z(g,N);c!==void 0&&(t=w(t,c))}}if(t.rules){let u=Object.keys(t.rules).sort().map(s=>`"${C.bold.green(s)}"`);m.info(`Loaded ${C.bold(u.length)} mdat comment expansion ${L("rule",u.length)}:`);for(let s of u)m.info(` ${s}`)}else m.error("No rules loaded from additional configurations or rules, using default.");return t}function z(e,o){if(o.safeParse(e).success)return{rules:e};m.error(`Rules object has the wrong shape. Ignoring and using default configuration:
|
|
3
|
+
${JSON.stringify(e,void 0,2)}`)}function k(e,o){if(o.safeParse(e).success)return e;m.error(`Config object has the wrong shape. Ignoring and using default configuration:
|
|
4
|
+
${JSON.stringify(e,void 0,2)}`)}import D from"node:fs";import h from"node:path";import{isFileSync as M}from"path-type";import Z from"untildify";function U(e,o){let n=o===0?1:Math.floor(Math.log10(Math.abs(o))+1);return e.toString().padStart(n,"0")}function $(e,o,n,d){let r=[];for(let[t,a]of e.entries()){let f=n&&e.length>1?`-${U(t+1,e.length+1)}`:"",p=E(a,o,n,d,f);r.push(p)}return r}function E(e,o,n,d,r=""){let t=P(e),a=o?P(o):void 0;if(!M(t))throw new Error(`Input file not found: "${t}"`);if(a){if(M(a))throw new Error(`Output path must be a directory, received a file path: "${a}"`);D.mkdirSync(a,{recursive:!0})}let f=n?h.basename(n,h.extname(n)):h.basename(t,h.extname(t)),p=`.${d??(n&&h.extname(n)!==""?h.extname(n):h.extname(e)===""?"":h.extname(e))}`,u=`${f}${r}${p}`,s=a??h.dirname(t);return{input:t,name:u,output:s}}function P(e){return Z(e)}import{remark as W}from"remark";import q from"remark-gfm";import{default as B}from"remark-mdat";import{read as G}from"to-vfile";import{VFile as Me}from"vfile";async function j(e,o,n,d,r){let t=await v({additionalConfig:d,additionalRules:r}),a=$(e,n,o,"md"),f=[],p=Y(t);for(let{input:u,name:s,output:i}of a){let g=await G(u),c=await p.process(g);c.dirname=i,c.basename=s,f.push(c)}return f}function Y(e){return W().use({settings:{bullet:"-",emphasis:"_"}}).use(q).use(B,e??{})}import _ from"plur";import H from"pretty-ms";import{getMdatReports as Q,log as y,reporterMdat as X}from"remark-mdat";import{write as K}from"to-vfile";import ee from"yargs";import{hideBin as ne}from"yargs/helpers";var te=performance.now(),T=ee(ne(process.argv));try{await T.scriptName("mdat").usage("$0 [command] [options]","Note: `expand` is the default and only command at the moment.").command(["$0 <files..> [options]","expand <files..> [options]"],"Expand comment placeholders in Markdown files.",e=>e.positional("files",{array:!0,demandOption:!0,describe:"Markdown file(s) with `mdat` placeholder comments to expand.",type:"string"}).option("config",{defaultDescription:"Configuration is loaded if found from the usual places, or defaults are used.",description:"Path(s) to files containing mdat configs.",string:!0,type:"array"}).option("rules",{alias:"r",description:"Path(s) to files containing `mdat` comment expansion rules.",string:!0,type:"array"}).option("output",{alias:"o",defaultDescription:"Same directory as input file.",description:"Output file directory.",type:"string"}).option("name",{alias:"n",defaultDescription:"Same name as input file. Overwrites the input file.",description:"Output file name.",type:"string"}).option("print",{default:!1,description:"Print the expanded Markdown to stdout instead of saving to a file. Ignores `--output` and `--name` options.",type:"boolean"}).option("prefix",{description:"Require a string prefix before all comments to be considered for expansion. Useful if you have a bunch of non-`mdat` comments in your Markdown file, or if you're willing to trade some verbosity for safety.",type:"string"}).option("meta",{alias:"m",default:!1,description:"Embed an extra comment at the top of the generated Markdown noting the date of generation and warning editors that certain sections of the document have been generated dynamically.",type:"boolean"}).option("check",{alias:"c",default:!1,describe:"Check the input files for rule violations without expanding them. Identifies things like missing comment placeholders and incorrect placeholder ordering.",type:"boolean"}).option("verbose",{default:!1,describe:"Enable verbose logging. All verbose logs and prefixed with their log level and are printed to stderr for ease of redirection.",type:"boolean"}),async({check:e,config:o,files:n,meta:d,name:r,output:t,prefix:a="",print:f,rules:p,verbose:u})=>{y.verbose=u,e&&(t&&(t=void 0,y.warn("Ignoring --output option because --check is set")),r&&(r=void 0,y.warn("Ignoring --name option because --check is set")),f&&(f=!1,y.warn("Ignoring --print option because --check is set"))),f&&(t&&(t=void 0,y.warn("Ignoring --output option because --print is set")),r&&(r=void 0,y.warn("Ignoring --name option because --print is set")));let s=[...o??[],{addMetaComment:d,keywordPrefix:a}];Array.isArray(n)||(n=[n]);let i=await j(n,r,t,s,p);if(f)for(let l of i)process.stdout.write(l.toString());if(X(i),!e&&!f)for(let l of i)await K(l);let c=Q(i).reduce((l,x)=>l+x.errors.length,0);y.info(`Expanded comments in ${n.length} ${_("file",n.length)} in ${H(performance.now()-te)}.`),process.exitCode=c>0?1:0}).help().alias("h","help").version().alias("v","version").wrap(process.stdout.isTTY?Math.min(120,T.terminalWidth()):0).fail(!1).parse()}catch(e){e instanceof Error&&y.error(e.message),process.exitCode=1}
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Options as MdatOptions, type Rules } from 'remark-mdat';
|
|
2
|
+
import { VFile } from 'vfile';
|
|
3
|
+
/**
|
|
4
|
+
* Generously accept either string paths to .ts, .js, or .json files with
|
|
5
|
+
* `MdatOptions` type default exports. Takes a single value, or an array of values which
|
|
6
|
+
* will be merged right to left.
|
|
7
|
+
*/
|
|
8
|
+
export type ExpandConfig<T extends MdatOptions = MdatOptions> = Array<T | string> | T | string;
|
|
9
|
+
/**
|
|
10
|
+
* Generously accept either string paths to .ts, .js, or .json files with
|
|
11
|
+
* `Rules` type default exports.
|
|
12
|
+
*/
|
|
13
|
+
export type ExpandRules = Array<Rules | string> | Rules | string;
|
|
14
|
+
/**
|
|
15
|
+
* Writing is the responsibility of the caller (e.g. via `await write(result)`)
|
|
16
|
+
*/
|
|
17
|
+
export declare function expandFiles(files: string[], name?: string, output?: string, config?: ExpandConfig, rules?: ExpandRules): Promise<VFile[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Writing is the responsibility of the caller (e.g. via `await write(result)`)
|
|
20
|
+
*/
|
|
21
|
+
export declare function expandFile(file: string, name?: string, output?: string, config?: ExpandConfig, rules?: ExpandRules): Promise<VFile>;
|
|
22
|
+
export declare function expandString(markdown: string, config?: ExpandConfig, rules?: ExpandRules): Promise<VFile>;
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type Options, type Rules } from 'remark-mdat';
|
|
2
|
+
import { type z } from 'zod';
|
|
3
|
+
export type MdatConfig = Options;
|
|
4
|
+
/**
|
|
5
|
+
* Load and validate mdat configuration / rule sets
|
|
6
|
+
* Uses cosmiconfig to search in the usual places.
|
|
7
|
+
* Merge precedence: Additional Config Paths < Search Path < mdat-remark defaults
|
|
8
|
+
*
|
|
9
|
+
* Generic to accommodate additional Config options, so set T to your custom config type if needed. You must provide a matching configExtensionSchema as well.
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadConfig<T extends MdatConfig>(options?: {
|
|
12
|
+
/**
|
|
13
|
+
* Additional Config objects to merge.
|
|
14
|
+
*
|
|
15
|
+
* Strings are treated as paths to `ts`, `js`, or `json` files with `Config` type default exports. These will be dynamically loaded by Cosmiconfig.
|
|
16
|
+
* Accepts an individual item, or an array. Objects in the array will be merged right to left.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
additionalConfig?: Array<T | string> | T | string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Additional Rules objects to merge.
|
|
22
|
+
*
|
|
23
|
+
* Strings are treated as paths to `ts`, `js`, or `json` files with `Rules` type default exports. These will be dynamically loaded by Cosmiconfig.
|
|
24
|
+
* Accepts an individual item, or an array. Objects in the array will be merged right to left, and take precedence over any rules in previously loaded Config objects as well.
|
|
25
|
+
*/
|
|
26
|
+
additionalRules?: Array<Rules | string> | Rules | string | undefined;
|
|
27
|
+
/** Additional zod schema that will be merged with the top level of the config schema and used for config validation. Default mdat-remark options schema is used if not provided. Useful if extending config for things like mdat-readme. */
|
|
28
|
+
configExtensionSchema?: z.ZodObject<any>;
|
|
29
|
+
/** Search for config in specific directories, mainly useful for testing. Cosmiconfig default search paths used if unset. */
|
|
30
|
+
searchFrom?: string;
|
|
31
|
+
}): Promise<T>;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{defaultLoaders as M}from"cosmiconfig";function O(n,e){let t=M[".json"],r=t(n,e);return w(r)}function w(n,e="",t={}){for(let[r,s]of Object.entries(n)){let o=e?`${e}.${r}`:r;typeof s=="object"&&s!==null&&!Array.isArray(s)?w(s,o,t):s===null?t[o]="null":t[o]=s.toString()}return t}import j from"chalk";import{cosmiconfig as F}from"cosmiconfig";import k from"node:fs/promises";import v from"node:path";import L from"plur";import{deepMergeDefined as b,log as p,optionsSchema as V,rulesSchema as I}from"remark-mdat";async function x(n){let{additionalConfig:e,additionalRules:t,configExtensionSchema:r,searchFrom:s}=n??{},o={},i=V;r!==void 0&&(i=i.merge(r));let l=F("mdat"),d=await l.search(s);if(d){let{config:u,filepath:f}=d;p.info(`Using config from "${f}"`);let a=P(u,i);a&&(o=b(o,a))}if(e!==void 0){let u=Array.isArray(e)?e:[e];for(let f of u){let a;if(typeof f=="string"){let c=await l.load(f);if(c==null)continue;let{config:h,filepath:y}=c;p.info(`Loaded additional config from "${y}"`),a=h}else a=f;if(a===void 0)continue;p.info("Merging configuration object");let g=P(a,i);g!==void 0&&(o=b(o,g))}}if(t!==void 0){let u=Array.isArray(t)?t:[t],f=F("mdat",{loaders:{".json":O}});for(let a of u){let g;if(typeof a=="string"){let h;if(v.basename(a).endsWith("package.json")){let J=await k.readFile(a,"utf8");h={config:O(a,J),filepath:a}}else h=await f.load(a);if(h==null)continue;let{config:y,filepath:A}=h;p.info(`Loaded additional config from "${A}"`),g=y}else g=a;if(g===void 0)continue;p.info("Merging rules into configuration object");let c=z(g,I);c!==void 0&&(o=b(o,c))}}if(o.rules){let u=Object.keys(o.rules).sort().map(f=>`"${j.bold.green(f)}"`);p.info(`Loaded ${j.bold(u.length)} mdat comment expansion ${L("rule",u.length)}:`);for(let f of u)p.info(` ${f}`)}else p.error("No rules loaded from additional configurations or rules, using default.");return o}function z(n,e){if(e.safeParse(n).success)return{rules:n};p.error(`Rules object has the wrong shape. Ignoring and using default configuration:
|
|
2
|
+
${JSON.stringify(n,void 0,2)}`)}function P(n,e){if(e.safeParse(n).success)return n;p.error(`Config object has the wrong shape. Ignoring and using default configuration:
|
|
3
|
+
${JSON.stringify(n,void 0,2)}`)}import N from"node:fs";import m from"node:path";import{isFileSync as $}from"path-type";import Z from"untildify";function D(n,e){let t=e===0?1:Math.floor(Math.log10(Math.abs(e))+1);return n.toString().padStart(t,"0")}function S(n,e,t,r){let s=[];for(let[o,i]of n.entries()){let l=t&&n.length>1?`-${D(o+1,n.length+1)}`:"",d=R(i,e,t,r,l);s.push(d)}return s}function R(n,e,t,r,s=""){let o=E(n),i=e?E(e):void 0;if(!$(o))throw new Error(`Input file not found: "${o}"`);if(i){if($(i))throw new Error(`Output path must be a directory, received a file path: "${i}"`);N.mkdirSync(i,{recursive:!0})}let l=t?m.basename(t,m.extname(t)):m.basename(o,m.extname(o)),d=`.${r??(t&&m.extname(t)!==""?m.extname(t):m.extname(n)===""?"":m.extname(n))}`,u=`${l}${s}${d}`,f=i??m.dirname(o);return{input:o,name:u,output:f}}function E(n){return Z(n)}import{remark as G}from"remark";import U from"remark-gfm";import{default as W}from"remark-mdat";import{read as T}from"to-vfile";import{VFile as _}from"vfile";async function q(n,e,t,r,s){let o=await x({additionalConfig:r,additionalRules:s}),i=S(n,t,e,"md"),l=[],d=C(o);for(let{input:u,name:f,output:a}of i){let g=await T(u),c=await d.process(g);c.dirname=a,c.basename=f,l.push(c)}return l}async function B(n,e,t,r,s){let o=await x({additionalConfig:r,additionalRules:s}),i=R(n,t,e,"md"),l=await T(i.input),d=await C(o).process(l);return d.dirname=i.output,d.basename=i.name,d}async function H(n,e,t){let r=await x({additionalConfig:e,additionalRules:t});return C(r).process(new _(n))}function C(n){return G().use({settings:{bullet:"-",emphasis:"_"}}).use(U).use(W,n??{})}export{B as expandFile,q as expandFiles,H as expandString,x as loadConfig};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function getInputOutputPaths(inputs: string[], output: string | undefined, name: string | undefined, extension: string | undefined): Array<{
|
|
2
|
+
input: string;
|
|
3
|
+
name: string;
|
|
4
|
+
output: string;
|
|
5
|
+
}>;
|
|
6
|
+
export declare function getInputOutputPath(input: string, output: string | undefined, name: string | undefined, extension: string | undefined, nameSuffix?: string): {
|
|
7
|
+
input: string;
|
|
8
|
+
name: string;
|
|
9
|
+
output: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function expandPath(file: string): string;
|
package/license.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Eric Mika
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mdat",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "CLI tool and library for using comments as content templates in Markdown files.",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git@github.com:kitschpatrol/mdat.git",
|
|
9
|
+
"directory": "packages/mdat"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/kitschpatrol/mdat/issues",
|
|
13
|
+
"email": "eric@ericmika.com"
|
|
14
|
+
},
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Eric Mika",
|
|
17
|
+
"email": "eric@ericmika.com",
|
|
18
|
+
"url": "https://ericmika.com"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18.0.0",
|
|
23
|
+
"pnpm": ">=8.0.0"
|
|
24
|
+
},
|
|
25
|
+
"bin": {
|
|
26
|
+
"mdat": "bin/cli.js"
|
|
27
|
+
},
|
|
28
|
+
"main": "./dist/index.js",
|
|
29
|
+
"module": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"files": [
|
|
32
|
+
"bin/*",
|
|
33
|
+
"dist/*"
|
|
34
|
+
],
|
|
35
|
+
"keywords": [
|
|
36
|
+
"mdat",
|
|
37
|
+
"markdown",
|
|
38
|
+
"template",
|
|
39
|
+
"comments",
|
|
40
|
+
"docs-generator",
|
|
41
|
+
"docs",
|
|
42
|
+
"cli"
|
|
43
|
+
],
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@types/mdast": "^4.0.3",
|
|
46
|
+
"@types/unist": "^3.0.2",
|
|
47
|
+
"chalk": "^5.3.0",
|
|
48
|
+
"cosmiconfig": "^9.0.0",
|
|
49
|
+
"path-type": "^5.0.0",
|
|
50
|
+
"plur": "^5.1.0",
|
|
51
|
+
"pretty-ms": "^9.0.0",
|
|
52
|
+
"remark": "^15.0.1",
|
|
53
|
+
"remark-gfm": "^4.0.0",
|
|
54
|
+
"to-vfile": "^8.0.0",
|
|
55
|
+
"type-fest": "^4.10.2",
|
|
56
|
+
"untildify": "^5.0.0",
|
|
57
|
+
"vfile": "^6.0.1",
|
|
58
|
+
"yargs": "^17.7.2",
|
|
59
|
+
"zod": "^3.22.4",
|
|
60
|
+
"remark-mdat": "0.1.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/node": "^20.11.14",
|
|
64
|
+
"@types/yargs": "^17.0.32",
|
|
65
|
+
"execa": "^8.0.1",
|
|
66
|
+
"nanoid": "^5.0.5",
|
|
67
|
+
"tsup": "^8.0.1",
|
|
68
|
+
"typescript": "^5.3.3",
|
|
69
|
+
"vitest": "^1.2.2"
|
|
70
|
+
},
|
|
71
|
+
"publishConfig": {
|
|
72
|
+
"access": "public"
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"build": "tsup && tsc -p tsconfig.build.json",
|
|
76
|
+
"dev": "pnpm run test",
|
|
77
|
+
"mdat": "../mdat-readme/bin/cli.js --config ../../mdat.config.ts",
|
|
78
|
+
"pretest": "pnpm run build",
|
|
79
|
+
"test": "vitest"
|
|
80
|
+
}
|
|
81
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
<!--+ Warning: Content in HTML comment blocks generated by mdat on 2024-02-08 +-->
|
|
2
|
+
|
|
3
|
+
<!-- header -->
|
|
4
|
+
|
|
5
|
+
# mdat
|
|
6
|
+
|
|
7
|
+
[](https://npmjs.com/package/mdat)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
|
|
10
|
+
**CLI tool and library for using comments as content templates in Markdown files.**
|
|
11
|
+
|
|
12
|
+
<!-- /header -->
|
|
13
|
+
|
|
14
|
+
> \[!NOTE]\
|
|
15
|
+
> **Please see the [Mdat Monorepo readme](http://github.com/kitschpatrol/mdat) for additional context.**
|
|
16
|
+
|
|
17
|
+
<!-- table-of-contents -->
|
|
18
|
+
|
|
19
|
+
## Table of contents
|
|
20
|
+
|
|
21
|
+
- [Overview](#overview)
|
|
22
|
+
- [Getting started](#getting-started)
|
|
23
|
+
- [Dependencies](#dependencies)
|
|
24
|
+
- [Installation](#installation)
|
|
25
|
+
- [Usage](#usage)
|
|
26
|
+
- [CLI](#cli)
|
|
27
|
+
- [API](#api)
|
|
28
|
+
- [Configuration](#configuration)
|
|
29
|
+
- [Creating custom rules](#creating-custom-rules)
|
|
30
|
+
- [Maintainers](#maintainers)
|
|
31
|
+
- [Acknowledgements](#acknowledgements)
|
|
32
|
+
- [Contributing](#contributing)
|
|
33
|
+
- [License](#license)
|
|
34
|
+
|
|
35
|
+
<!-- /table-of-contents -->
|
|
36
|
+
|
|
37
|
+
## Overview
|
|
38
|
+
|
|
39
|
+
This is a CLI tool and library implementing the Markdown Autophagic Template (mdat) system, which makes it easy to automate the replacement of placeholder comments in Markdown documents with dynamic content from a variety of sources. `mdat` also validates the structure and content of the Markdown document based on constraints specified in the expansion rules.
|
|
40
|
+
|
|
41
|
+
A trivial example...
|
|
42
|
+
|
|
43
|
+
Given placeholder comments in a Markdown file like this:
|
|
44
|
+
|
|
45
|
+
`some-file.md`
|
|
46
|
+
|
|
47
|
+
```md
|
|
48
|
+
<!-- title -->
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Run your file through the tool:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
mdat some-file.md
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
To turn it into:
|
|
58
|
+
|
|
59
|
+
`some-file.md`
|
|
60
|
+
|
|
61
|
+
```md
|
|
62
|
+
<!-- title -->
|
|
63
|
+
|
|
64
|
+
# mdat
|
|
65
|
+
|
|
66
|
+
<!-- /title -->
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
In this case, according to a set of rules defined in an external configuration file, `<!-- title -->` was replaced with date from `package.json`. The rule system behind these expansions is simple to define and readily extensible beyond the trivial example above.
|
|
70
|
+
|
|
71
|
+
The `mdat` CLI and library package sits between the lower-level \[`remark-mdat`] remark plugin, and the higher-level \[`mdat-readme`] package. `mdat` offers flexibility, but if you're just looking to expand some placeholders in your readme files then [`mdat-readme`](../mdat-readme) is the place to start.
|
|
72
|
+
|
|
73
|
+
## Getting started
|
|
74
|
+
|
|
75
|
+
### Dependencies
|
|
76
|
+
|
|
77
|
+
The `mdat` CLI tool requires Node 16+. The exported APIs for expanding Markdown text and documents are ESM-only and share the Node 16+ requirement. `mdat` is implemented in TypeScript and bundles a complete set of type definitions.
|
|
78
|
+
|
|
79
|
+
### Installation
|
|
80
|
+
|
|
81
|
+
Install locally to access the CLI commands in a single project or to import the provided APIs:
|
|
82
|
+
|
|
83
|
+
```sh
|
|
84
|
+
npm install remark-mdat
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Or, install globally for access across your system:
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
npm install --global remark-mdat
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Usage
|
|
94
|
+
|
|
95
|
+
> \[!WARNING]\
|
|
96
|
+
> **The `mdat` CLI tool directly manipulates the contents of readme files, in close (and perhaps dangerous) proximity to your painstakingly crafted words.**
|
|
97
|
+
>
|
|
98
|
+
> Please make sure any text you care about is committed before running `mdat`, and never directly modify content inside of the comment expansion blocks.
|
|
99
|
+
>
|
|
100
|
+
> Set the `--meta` flag on the command to add a warning comment to the top of your file explaining the extra caution demanded around the volatile automated sections of your readme.md.
|
|
101
|
+
|
|
102
|
+
### CLI
|
|
103
|
+
|
|
104
|
+
<!-- cli-help -->
|
|
105
|
+
|
|
106
|
+
#### Command: `mdat`
|
|
107
|
+
|
|
108
|
+
Note: `expand` is the default and only command at the moment.
|
|
109
|
+
|
|
110
|
+
This section lists top-level commands for `mdat`.
|
|
111
|
+
|
|
112
|
+
If no command is provided, `mdat expand` is run by default.
|
|
113
|
+
|
|
114
|
+
Usage:
|
|
115
|
+
|
|
116
|
+
```txt
|
|
117
|
+
mdat [command] [options]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
| Command | Argument | Description |
|
|
121
|
+
| -------- | ----------------------- | ------------------------------------------------------------------- |
|
|
122
|
+
| `expand` | `<files..>` `[options]` | Expand comment placeholders in Markdown files. _(Default command.)_ |
|
|
123
|
+
|
|
124
|
+
_See the sections below for more information on each subcommand._
|
|
125
|
+
|
|
126
|
+
#### Subcommand: `mdat expand`
|
|
127
|
+
|
|
128
|
+
Expand comment placeholders in Markdown files.
|
|
129
|
+
|
|
130
|
+
Usage:
|
|
131
|
+
|
|
132
|
+
```txt
|
|
133
|
+
mdat expand <files..> [options]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
| Positional Argument | Description | Type |
|
|
137
|
+
| ------------------- | -------------------------------------------------------------------------- | -------- |
|
|
138
|
+
| `files` | Markdown file(s) with `mdat` placeholder comments to expand. _(Required.)_ | `string` |
|
|
139
|
+
|
|
140
|
+
| Option | Alias | Description | Type | Default |
|
|
141
|
+
| ----------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------- |
|
|
142
|
+
| `--config` | | Path(s) to files containing mdat configs. | `array` | Configuration is loaded if found from the usual places, or defaults are used. |
|
|
143
|
+
| `--rules` | `-r` | Path(s) to files containing `mdat` comment expansion rules. | `array` | |
|
|
144
|
+
| `--output` | `-o` | Output file directory. | `string` | Same directory as input file. |
|
|
145
|
+
| `--name` | `-n` | Output file name. | `string` | Same name as input file. Overwrites the input file. |
|
|
146
|
+
| `--print` | | Print the expanded Markdown to stdout instead of saving to a file. Ignores `--output` and `--name` options. | `boolean` | `false` |
|
|
147
|
+
| `--prefix` | | Require a string prefix before all comments to be considered for expansion. Useful if you have a bunch of non-`mdat` comments in your Markdown file, or if you're willing to trade some verbosity for safety. | `string` | |
|
|
148
|
+
| `--meta` | `-m` | Embed an extra comment at the top of the generated Markdown noting the date of generation and warning editors that certain sections of the document have been generated dynamically. | `boolean` | `false` |
|
|
149
|
+
| `--check` | `-c` | Check the input files for rule violations without expanding them. Identifies things like missing comment placeholders and incorrect placeholder ordering. | `boolean` | `false` |
|
|
150
|
+
| `--verbose` | | Enable verbose logging. All verbose logs and prefixed with their log level and are printed to stderr for ease of redirection. | `boolean` | `false` |
|
|
151
|
+
| `--help` | `-h` | Show help | `boolean` | |
|
|
152
|
+
| `--version` | `-v` | Show version number | `boolean` | |
|
|
153
|
+
|
|
154
|
+
<!-- /cli-help -->
|
|
155
|
+
|
|
156
|
+
<!-- cli-help-note -->
|
|
157
|
+
|
|
158
|
+
_Meta note: The entire section above was generated automatically by the [`<!-- cli-help -->`](../mdat-readme/src/lib/rules/cli-help/index.ts) mdat expansion rule provided in `mdat-readme`. It dynamically parses the output from `mdat --help` into a markdown table, recursively calling `--help` on subcommands to build a Markdown representation of the help output._
|
|
159
|
+
|
|
160
|
+
<!-- /cli-help-note -->
|
|
161
|
+
|
|
162
|
+
#### Examples
|
|
163
|
+
|
|
164
|
+
##### Basic
|
|
165
|
+
|
|
166
|
+
Expand comments in a single Markdown file in-place:
|
|
167
|
+
|
|
168
|
+
```sh
|
|
169
|
+
mdat your-file.md
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
##### Multi-file
|
|
173
|
+
|
|
174
|
+
Expand comments in multiple Markdown files:
|
|
175
|
+
|
|
176
|
+
```sh
|
|
177
|
+
mdat *.md
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
##### Custom configuration
|
|
181
|
+
|
|
182
|
+
A number of option flags are exposed on the CLI. Any values set here will override both ambient configuration files and any configuration file referenced passed as options:
|
|
183
|
+
|
|
184
|
+
```sh
|
|
185
|
+
mdat --prefix 'mm-
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
##### Custom configuration file
|
|
189
|
+
|
|
190
|
+
```sh
|
|
191
|
+
mdat --config 'custom-config.ts"
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
##### Rule sets
|
|
195
|
+
|
|
196
|
+
```sh
|
|
197
|
+
mdat --rules 'rules.ts' 'more-rules.js' 'yet-more-rules.json'
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### API
|
|
201
|
+
|
|
202
|
+
`mdat` exports a collection of functions to abstract the process of expanding placeholder comments into a single call. Type aliases are also provided.
|
|
203
|
+
|
|
204
|
+
Highlights include:
|
|
205
|
+
|
|
206
|
+
#### Expand String
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
function expandString(markdown: string, config?: ExpandConfig, rules?: ExpandRules): Promise<VFile>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Takes a string of Markdown and returns. Note that the returned object is a [VFile](https://github.com/vfile), which includes both the post-conversion Markdown content and additional metadata about the conversion.
|
|
213
|
+
|
|
214
|
+
To get the Markdown content, simply call `.toString()` on the returned VFile object.
|
|
215
|
+
|
|
216
|
+
#### Expand File
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
function expandFile(
|
|
220
|
+
file: string,
|
|
221
|
+
name?: string,
|
|
222
|
+
output?: string,
|
|
223
|
+
config?: ExpandConfig,
|
|
224
|
+
rules?: ExpandRules,
|
|
225
|
+
): Promise<VFile>
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Similar to `expandString()`, but takes a file path and handles setting an optional destination path and file name.
|
|
229
|
+
|
|
230
|
+
It's up to the caller to actually save the returned VFile object. The [to-vfile](https://www.npmjs.com/package/to-vfile) library can make this particularly painless:
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import { expandFile } from 'mdat'
|
|
234
|
+
import { write } from 'to-vfile'
|
|
235
|
+
|
|
236
|
+
const file = await expandFiles(...)
|
|
237
|
+
await write(file)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
#### Expand Files
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
function expandFiles(
|
|
244
|
+
files: string[],
|
|
245
|
+
name?: string,
|
|
246
|
+
output?: string,
|
|
247
|
+
config?: ExpandConfig,
|
|
248
|
+
rules?: ExpandRules,
|
|
249
|
+
): Promise<VFile[]>
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Like `expandFile()`, but accepts an array of inputs. If an output name is specified, the output files are suffixed with a number to prevent name collisions.
|
|
253
|
+
|
|
254
|
+
#### Load Config
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
function loadConfig<T extends Partial<Options>>(options?: {
|
|
258
|
+
additionalConfig?: Array<T | string> | T | string | undefined
|
|
259
|
+
additionalRules?: Array<Rules | string> | Rules | string | undefined
|
|
260
|
+
configExtensionSchema?: z.ZodObject<any>
|
|
261
|
+
searchFrom?: string
|
|
262
|
+
}): Promise<T>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
This is provided for more advanced use cases. It assists in discovering and loading ambient configuration in your project (e.g. fields in your package.json, or dedicated `mdat` config files). It also dynamically loads, validates, and merges additional `mdat` configuration and rule files into a final `ExpandConfig` object ready to be passed into the [`remark-mdat`](../remark-mdat/readme.md) plugin or one of the API functions like `expandFile()`.
|
|
266
|
+
|
|
267
|
+
The function is generic to accommodate extensions to the configuration type in derivative projects. ([`mdat-readme`]() uses this approach.)
|
|
268
|
+
|
|
269
|
+
#### Examples
|
|
270
|
+
|
|
271
|
+
### Configuration
|
|
272
|
+
|
|
273
|
+
Expansion rules and certain aspects of global configuration are defined in configuration files which may be discovered automatically by `mdat`, or explicitly provided via command line options or library function arguments as shown above.
|
|
274
|
+
|
|
275
|
+
`mdat` implements configuration loading via [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig), which means a variety of configuration [locations](https://github.com/cosmiconfig/cosmiconfig?tab=readme-ov-file#searchplaces) and file formats are supported. Configuration may be defined directly in your package.json, or in addition to stand-alone TypeScript files, JavaScript files, YAML, JSON, etc.
|
|
276
|
+
|
|
277
|
+
TypeScript or JavaScript with JSDoc annotations are recommended for the most flexibility and ease of implementing more advanced rules.
|
|
278
|
+
|
|
279
|
+
`mdat` also allows arbitrary JSON files to be loaded as rule sets, flattening them so any value may be accessed by using a dot-notation key path as a comment keyword.
|
|
280
|
+
|
|
281
|
+
#### Configuration file format
|
|
282
|
+
|
|
283
|
+
The `mdat` configuration file is a record object allowing you to customize aspects of the comment expansion process, and also optionally define expansion rules as well under the `rules` key:
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
type MdatConfig = {
|
|
287
|
+
addMetaComment?: boolean
|
|
288
|
+
closingPrefix?: string
|
|
289
|
+
keywordPrefix?: string
|
|
290
|
+
metaCommentIdentifier?: string
|
|
291
|
+
rules?: Rules
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
A valid configuration file default-exports an object conforming to the above type.
|
|
296
|
+
|
|
297
|
+
The configuration file may be located in any location supported by [cosmicconfig](https://github.com/cosmiconfig/cosmiconfig?tab=readme-ov-file#searchplaces). I use a `mdat.config.ts` file in the root of my projects.
|
|
298
|
+
|
|
299
|
+
#### Rule file format
|
|
300
|
+
|
|
301
|
+
Rules may also be defined in separate files that default-export a record of rules. The record keys become the keywords used to reference a rule from your comments in Markdown.
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
type Rules = Record<string, Rule>
|
|
305
|
+
|
|
306
|
+
type Rule =
|
|
307
|
+
| string
|
|
308
|
+
| ((options: JsonValue, tree: Root) => Promise<string> | string)
|
|
309
|
+
| Rule[]
|
|
310
|
+
| {
|
|
311
|
+
content: string | Rule[] | ((options: JsonValue, tree: Root) => Promise<string> | string)
|
|
312
|
+
applicationOrder?: number | undefined
|
|
313
|
+
order?: number | undefined
|
|
314
|
+
required?: boolean | undefined
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
This is a bit complex, but it's indented to make defining simple rules simple, while still accommodating more demanding use cases.
|
|
319
|
+
|
|
320
|
+
Some notes on the type:
|
|
321
|
+
|
|
322
|
+
- Simple rules can be defined directly on the key, either as strings to replace the comment placeholder, or as sync or async functions returning a string.
|
|
323
|
+
|
|
324
|
+
- If you need more advanced rules, or wish to define conditions for the validation process, you break the top-level keyword key's value out into an object, where a `content` key on the object is responsible for returning the replacement string, and additional fields are available to define validation constraints.
|
|
325
|
+
|
|
326
|
+
- Note that `content` can itself take an array of Rule objects, which is useful for creating "compound" rules that combine several others into a single comment keyword.
|
|
327
|
+
|
|
328
|
+
Since it's a record, multiple rules may be combined in single rules file.
|
|
329
|
+
|
|
330
|
+
If multiple configuration and rule files are loaded, they are merged. CLI options take precedence over ambient configuration discovered by cosmicconfig, and where multiple configuration or rule files are provided, with the "last" rule of a particular key takes precedence.
|
|
331
|
+
|
|
332
|
+
## Creating custom rules
|
|
333
|
+
|
|
334
|
+
See the [Examples section](../remark-mdat/readme.md#examples) of the `remark-mdat` readme, or take a look at the implementation of the [rules bundled with `mdat-readme`](../mdat-readme/src/lib/rules/) for more complex examples.
|
|
335
|
+
|
|
336
|
+
## Maintainers
|
|
337
|
+
|
|
338
|
+
[@kitschpatrol](https://github.com/kitschpatrol)
|
|
339
|
+
|
|
340
|
+
## Acknowledgements
|
|
341
|
+
|
|
342
|
+
Please see the [monorepo readme](../../readme.md#acknowledgments).
|
|
343
|
+
|
|
344
|
+
<!-- footer -->
|
|
345
|
+
|
|
346
|
+
## Contributing
|
|
347
|
+
|
|
348
|
+
[Issues](https://github.com/kitschpatrol/mdat/issues) and pull requests are welcome.
|
|
349
|
+
|
|
350
|
+
## License
|
|
351
|
+
|
|
352
|
+
[MIT](license.txt) © Eric Mika
|
|
353
|
+
|
|
354
|
+
<!-- /footer -->
|