Skip to content

@dovenv/core - Examples

CLI

bash
dovenv -h

Run dovenv with a custom config file

bash
dovenv -c my/custom/config.js

Check your workspace files/dirs/code

bash
dovenv check

Tranform your workspace files/dirs

bash
dovenv transform

Config file

ModuleJS file

js
import { defineConfig } from '../src/main'

export default defineConfig( {
	name : 'MJS WORKSPACE',
	desc : 'This is a project workspace MJS example.',
} )

CommonJS file

js
const { defineConfig } = require( '../dist/main.mjs' )

module.exports = defineConfig( {
	name : 'CJS WORKSPACE',
	desc : 'This is a project workspace CJS example.',
} )

Library example

This is a example that contains dovenv instance in a TS file

Instance (JavaScript/TypeScript)

ts
import {
	getCurrentDir,
	hideBin,
} from '@dovenv/utils'

import config       from './config'
import { joinPath } from '../../utils/src/sys/main'
import { Dovenv }   from '../src/main'

const dovenv = new Dovenv( { config } )

// In a JS instance of DOVENV, dovenvConfigPath will be undefined, so you can set the config path manually
// 'dovenvConfigPath' property is used for user information purposes only
dovenv.dovenvConfigPath = joinPath( getCurrentDir( import.meta.url ), 'config.ts' )

await dovenv.run( hideBin( process.argv ) )

TypeScript

For use with TS at the momment. you need to has TypeScript installed. an run dovenv via TS

ModuleTS file

ts
import { defineConfig } from '../src/main'

export default defineConfig( {
	name : 'MTS WORKSPACE',
	desc : 'This is a project workspace MTS example.',
} )

CommonTS file

ts
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { defineConfig } = require( '../dist/main.mjs' )

module.exports = defineConfig( {
	name : 'CTS WORKSPACE',
	desc : 'This is a project workspace CTS example.',
} )

Run

ts
import {
	getCurrentDir,
	hideBin,
} from '@dovenv/utils'

import config       from './config'
import { joinPath } from '../../utils/src/sys/main'
import { Dovenv }   from '../src/main'

const dovenv = new Dovenv( { config } )

// In a JS instance of DOVENV, dovenvConfigPath will be undefined, so you can set the config path manually
// 'dovenvConfigPath' property is used for user information purposes only
dovenv.dovenvConfigPath = joinPath( getCurrentDir( import.meta.url ), 'config.ts' )

await dovenv.run( hideBin( process.argv ) )

Advanced

This is a example that contains a lot of configuration

Check specific key

bash
dovenv check -k packages

Check pattern key

bash
dovenv check -k 'package*'

Check multiple key

bash
dovenv check -k 'package*' 'docs'

Tranform specific key

bash
dovenv transform -k readme

Show constants

bash
dovenv const

Show alias

List aliases of your config file. Use "dovenv alias -k [alias]" to show a specific alias

bash
dovenv const

Execute alias. For example dovenv x struct

bash
dovenv x [alias-key]
bash
dovenv config

Config file

ts
import {
	existsDir,
	existsFile,
	joinPath,
	setDirTree,
	box,
	getObjectFrom,
	readFile,
	getCurrentDir,
	replacePlaceholders,
	getPaths,
	getBaseName,
	asciiFont,
} from '@dovenv/utils'

import pkg              from '../../../package.json'
import { version }      from '../package.json'
import { defineConfig } from '../src/main' // change it for @dovenv/core

const currentDir = getCurrentDir( import.meta.url )
const wsDir      = joinPath( currentDir, '..', '..', '..' ) // My workspace path

export default defineConfig( {
	name  : 'PROJECT WORKSPACE',
	desc  : 'This is a project workspace example.',
	const : {
		version,
		pkg,
		wsDir,
		mark       : `\n${asciiFont( `pigeonposse\n-------\n${pkg.name}`, 'ANSI Shadow' )}\n`,
		sctructure : '\n' + box( setDirTree( { structure : {
			'.vscode' : {
				'settings.json'   : null,
				'extensions.json' : null,
			},
			'docs'             : { '*.md': null },
			'packages'         : { '**': null },
			'.gitignore'       : null,
			'.pigeonposse.yml' : null,
			'LICENSE'          : null,
			'package.json'     : null,
			'README.md'        : null,
		} } ), {
			padding     : 1,
			// titleAlignment : 'center',
			title       : 'Workspace Structure',
			borderStyle : 'none',
			borderColor : 'gray',
			dimBorder   : true,
		} ),
		custom : async () => {

			const res = await getObjectFrom<{ web: Record<string, unknown>[] }>(
				'https://raw.githubusercontent.com/pigeonposse/super8/main/.pigeonposse.yml',
			)

			return res.web[0]

		},
		template : async () => {

			const templateDir                 = joinPath( currentDir, 'recourses/*' )
			const paths                       = await getPaths( [ templateDir ], { dot: true } )
			const res: Record<string, string> = {}
			for ( const path of paths ) {

				const key = getBaseName( path )
				res[key]  = await readFile( path, 'utf-8' )

			}

			return res

		},
	},
	/**
	 * Create custom commands
	 */
	custom : {
		/**
		 * Create simple command for show structure
		 */
		structure : {
			desc : 'Print structure for the workspace.',
			fn   : async ( { config } ) => console.log( config?.const?.sctructure ),
		},
		/**
		 * Create nested command
		 */
		greet : {
			desc : 'Say hello to username',
			cmds : {
				hello : {
					opts : { short : {
						type : 'boolean',
						desc : 'Say hello to username in short form',
					} },
					desc : 'Say hello to username',
					cmds : {
						es : { desc: 'Say hello to username in Spanish' },
						en : { desc: 'Say hello to username in English' },
					},
					examples : [
						{
							desc : 'say hello to John in Spanish',
							cmd  : '$0 greet hello es --name John',
						},
					],
				},
				bye : {
					opts : { time : {
						type  : 'boolean',
						alias : 't',
						desc  : 'Say goodbye to username in short form',
					} },
					desc : 'Say goodbye to username',
					cmds : {
						es : { desc: 'Say goodbye to username in Spanish' },
						en : { desc: 'Say goodbye to username in English' },
					},
					examples : [
						{
							desc : 'say goodbye to John in Spanish',
							cmd  : '$0 greet bye es --name John --time',
						},
					],
				},
			},
			opts : {
				name : {
					type    : 'string',
					alias   : 'n',
					desc    : 'Name of the user',
					default : 'John',
				},
				secondName : {
					type    : 'string',
					alias   : 's',
					desc    : 'Second name of the user',
					default : 'Doe',
				},
			},
			examples : [
				{
					desc : 'say hello to John',
					cmd  : '$0 greet hello --name John',
				},
				{
					desc : 'say Goodbye to John Doe',
					cmd  : '$0 greet bye --name John --secondName Doe',
				},
			],
			fn : async ( {
				opts, cmds,
			} ) => {

				const greet      = cmds?.includes( 'hello' )
					? cmds?.includes( 'es' ) ? 'Hola' : 'Hello'
					: cmds?.includes( 'es' ) ? 'Adios' : 'Goodbye'
				const secondName = cmds?.includes( 'hello' ) && opts?.short ? '' : opts?.secondName
				const time       = cmds?.includes( 'bye' ) && opts?.time ? '!!!' : ''

				console.log( `${greet} ${opts?.name} ${secondName}${time}` )

			},
		},
	},
	/**
	 * Create a alias for `struture` command
	 */
	alias : { struct : {
		desc : 'Set structure for the workspace',
		cmd  : 'dovenv structure --silent',
	} },
	/**
	 * Configuration for the transform command
	 */
	transform : {
		readme : {
			input : [ 'README.md' ],
			fn    : async props => {

				const mark = props.const?.mark
				return props.content + `\n<!--${mark}-->\n`

			},
		},
		pp : {
			input : [ '.pigeonposse.yml' ],
			fn    : async props => {

				const ppTemplate =  props.const?.template['.pigeonposse.yml']
				const content    = await replacePlaceholders( {
					content : ppTemplate,
					params  : props.const || {},
				} )
				return content

			},
		},
	},
	/**
	 * Configuration for the check command
	 */
	check : {
		packages : {
			desc     : 'Repo packages structure.',
			type     : 'dir',
			patterns : [
				'./packages/*',
				'!./packages/{plugin,config,theme}',
				'./packages/{plugin,config,theme}/*',
			],
			validateAll : async ( { paths } ) => {

				if ( paths.length === 0 ) throw new Error( 'No packages found' )

			},
			validate : async ( { path } ) => {

				// console.log( path, content )
				const files  = [ joinPath( path, 'README.md' ) ]
				const dirs   = [ joinPath( path, 'src' ) ]
				const noDirs = [ joinPath( path, 'source' ) ]

				for ( const file of files ) {

					const exists = await existsFile( file )
					if ( !exists ) throw new Error( `File [${file}] must exist` )

				}

				for ( const dir of dirs ) {

					const exists = await existsDir( dir )
					if ( !exists ) throw new Error( `Directory [${dir}] must exist` )

				}

				for ( const dir of noDirs ) {

					const exists = await existsDir( dir )
					if ( exists ) throw new Error( `Directory [${dir}] must not exist` )

				}

			},

		},
		docs : {
			desc        : 'Documentation structure.',
			type        : 'file',
			patterns    : [ 'docs/*.md' ],
			validateAll : async ( { paths } ) => {

				const validFiles = paths.filter( p =>
					p.endsWith( 'index.md' ) || p.endsWith( 'posts.md' ),
				)
				if ( validFiles.length !== 2 ) throw new Error( 'File [docs/index.md] and [docs/posts.md] must exist' )
				if ( paths.length < 3 ) throw new Error( `File [${paths}] must not exist` )

			},
			validate : async ( {
				path, content,
			} ) => {

				if ( !content ) throw new Error( `File [${path}] must exist` )

			},

		},
		monorepo : {
			desc        : 'Monorepo structure.',
			type        : 'file',
			patterns    : [ 'src/*' ],
			validateAll : async ( { paths } ) => {

				if ( paths.length ) throw new Error( `Directory [src] must not exist because it is a monorepo` )

			},

		},
		required : {
			desc     : 'Monorepo required files.',
			type     : 'file',
			patterns : [
				'LICENSE',
				'README.md',
				'.pigeonposse.yml',
				'package.json',
				'.gitignore',
				'.vscode/settings.json',
				'.vscode/extensions.json',
			],
			validateAll : async ( {
				paths, config,
			} ) => {

				if ( paths.length !== 7 )
					throw new Error( `Monorepo must have this structure:\n ${config?.const?.sctructure}` )

			},

		},
		packageJson : {
			desc     : 'Schema from repo packages.',
			type     : 'file',
			patterns : [ 'packages/*/package.json' ],
			validate : async ( {
				path, content,
			} ) => {

				if ( !content ) throw new Error( `File [${path}] must exist` )
				const c = JSON.parse( content )
				if ( !( 'version' in c ) ) throw new Error( `File [${path}] must have version field` )

			},
		},

	},
} )