Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/stale-kiwis-itch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"jsrepo": minor
---

feat: Optionally include documentation files (*.md, *.mdx) in registry

7 changes: 6 additions & 1 deletion schemas/project-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
"includeTests": {
"description": "When true includes the test files for each block in the same directory.",
"type": "boolean",
"default": "true"
"default": "false"
},
"includeDocs": {
"description": "When true includes the documentation files for each block in the same directory.",
"type": "boolean",
"default": "false"
},
"watermark": {
"description": "When true will add a watermark with the version and repository at the top of the installed files.",
Expand Down
5 changes: 5 additions & 0 deletions schemas/registry-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@
"type": "boolean",
"default": false
},
"includeDocs": {
"description": "Include documentation files (*.md, *.mdx) in the registry.",
"type": "boolean",
"default": false
},
"rules": {
"description": "Configure rules when checking manifest after build.",
"type": "object",
Expand Down
8 changes: 8 additions & 0 deletions src/commands/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import * as registry from '../utils/registry-providers/internal';
const schema = v.object({
watermark: v.optional(v.boolean()),
tests: v.optional(v.boolean()),
docs: v.optional(v.boolean()),
formatter: v.optional(v.union([v.literal('prettier'), v.literal('biome'), v.literal('none')])),
paths: v.optional(v.record(v.string(), v.string())),
expand: v.boolean(),
Expand Down Expand Up @@ -73,6 +74,11 @@ export const add = new Command('add')
.choices(['true', 'false'])
.argParser((val) => val === 'true')
)
.addOption(
new Option('--docs <choice>', 'Include docs when adding blocks.')
.choices(['true', 'false'])
.argParser((val) => val === 'true')
)
.option(
'--paths <category=path,category=path>',
'The paths where categories should be added to your project.',
Expand Down Expand Up @@ -146,6 +152,7 @@ async function _add(blockNames: string[], options: Options) {
config = {
$schema: '',
includeTests: false,
includeDocs: false,
watermark: true,
paths: {
'*': './src/blocks',
Expand All @@ -162,6 +169,7 @@ async function _add(blockNames: string[], options: Options) {
: config.formatter;
config.watermark = options.watermark !== undefined ? options.watermark : config.watermark;
config.includeTests = options.tests !== undefined ? options.tests : config.includeTests;
config.includeDocs = options.docs !== undefined ? options.docs : config.includeDocs;
config.paths =
options.paths !== undefined ? { ...config.paths, ...options.paths } : config.paths;

Expand Down
4 changes: 4 additions & 0 deletions src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const schema = v.object({
doNotListCategories: v.optional(v.array(v.string())),
allowSubdirectories: v.optional(v.boolean()),
preview: v.optional(v.boolean()),
includeDocs: v.optional(v.boolean()),
output: v.boolean(),
verbose: v.boolean(),
cwd: v.string(),
Expand Down Expand Up @@ -62,6 +63,7 @@ const build = new Command('build')
.option('--exclude-deps [deps...]', 'Dependencies that should not be added.')
.option('--allow-subdirectories', 'Allow subdirectories to be built.')
.option('--preview', 'Display a preview of the blocks list.')
.option('--include-docs', 'Include docs files (*.mdx, *.md) in the registry.')
.option('--no-output', `Do not output a \`${MANIFEST_FILE}\` file.`)
.option('--verbose', 'Include debug logs.', false)
.option('--cwd <path>', 'The current working directory.', process.cwd())
Expand Down Expand Up @@ -105,6 +107,7 @@ async function _build(options: Options) {
excludeCategories: options.excludeCategories ?? [],
allowSubdirectories: options.allowSubdirectories,
preview: options.preview,
includeDocs: options.includeDocs ?? false,
} satisfies RegistryConfig;
}

Expand All @@ -127,6 +130,7 @@ async function _build(options: Options) {
if (options.allowSubdirectories !== undefined)
mergedVal.allowSubdirectories = options.allowSubdirectories;
if (options.preview !== undefined) mergedVal.preview = options.preview;
if (options.includeDocs !== undefined) mergedVal.includeDocs = options.includeDocs;

mergedVal.rules = { ...DEFAULT_CONFIG, ...mergedVal.rules };

Expand Down
1 change: 1 addition & 0 deletions src/commands/exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export async function _exec(s: string | undefined, options: Options, command: an
config = {
$schema: '',
includeTests: false,
includeDocs: false,
watermark: true,
paths: {
'*': './',
Expand Down
7 changes: 7 additions & 0 deletions src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const schema = v.object({
repos: v.optional(v.array(v.string())),
watermark: v.boolean(),
tests: v.optional(v.boolean()),
docs: v.optional(v.boolean()),
formatter: v.optional(formatterSchema),
paths: v.optional(v.record(v.string(), v.string())),
configFiles: v.optional(v.record(v.string(), v.string())),
Expand All @@ -76,6 +77,7 @@ const init = new Command('init')
'Will not add a watermark to each file upon adding it to your project.'
)
.option('--tests', 'Will include tests with the blocks.')
.option('--docs', 'Will include docs with the blocks.')
.addOption(
new Option(
'--formatter <formatter>',
Expand Down Expand Up @@ -369,6 +371,10 @@ const _initProject = async (registries: string[], options: Options) => {
initialConfig.isOk() && options.tests === undefined
? initialConfig.unwrap().includeTests
: (options.tests ?? false),
includeDocs:
initialConfig.isOk() && options.docs === undefined
? initialConfig.unwrap().includeDocs
: (options.docs ?? false),
watermark: options.watermark,
formatter: options.formatter,
configFiles,
Expand Down Expand Up @@ -778,6 +784,7 @@ const _initRegistry = async (options: Options) => {
excludeBlocks: [],
excludeCategories: [],
preview: false,
includeDocs: false,
};
}

Expand Down
4 changes: 4 additions & 0 deletions src/commands/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const schema = v.object({
doNotListBlocks: v.optional(v.array(v.string())),
doNotListCategories: v.optional(v.array(v.string())),
allowSubdirectories: v.optional(v.boolean()),
includeDocs: v.optional(v.boolean()),
verbose: v.boolean(),
cwd: v.string(),
});
Expand Down Expand Up @@ -71,6 +72,7 @@ export const publish = new Command('publish')
)
.option('--exclude-deps [deps...]', 'Dependencies that should not be added.')
.option('--allow-subdirectories', 'Allow subdirectories to be built.')
.option('--include-docs', 'Include documentation files (*.md, *.mdx) in the registry.')
.option('--verbose', 'Include debug logs.', false)
.option('--cwd <path>', 'The current working directory.', process.cwd())
.action(async (opts) => {
Expand Down Expand Up @@ -110,6 +112,7 @@ async function _publish(options: Options) {
excludeBlocks: options.excludeBlocks ?? [],
excludeCategories: options.excludeCategories ?? [],
allowSubdirectories: options.allowSubdirectories,
includeDocs: options.includeDocs ?? false,
} satisfies RegistryConfig;
}

Expand All @@ -133,6 +136,7 @@ async function _publish(options: Options) {
if (options.excludeDeps) mergedVal.excludeDeps = options.excludeDeps;
if (options.allowSubdirectories !== undefined)
mergedVal.allowSubdirectories = options.allowSubdirectories;
if (options.includeDocs !== undefined) mergedVal.includeDocs = options.includeDocs;

mergedVal.rules = { ...DEFAULT_CONFIG, ...mergedVal.rules };

Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const blockSchema = v.object({
dependencies: v.array(v.string()),
devDependencies: v.array(v.string()),
tests: v.boolean(),
docs: v.optional(v.boolean(), false),
list: v.optional(v.boolean(), true),
/** Where to find the block relative to root */
directory: v.string(),
Expand Down
16 changes: 10 additions & 6 deletions src/utils/blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { Block } from '../types';
import * as array from './blocks/ts/array';
import { Err, Ok, type Result } from './blocks/ts/result';
import * as url from './blocks/ts/url';
import { isTestFile } from './build';
import { isDocsFile, isTestFile } from './build';
import { type Paths, type ProjectConfig, getPathForBlock, resolvePaths } from './config';
import * as registry from './registry-providers/internal';

Expand Down Expand Up @@ -148,15 +148,19 @@ type PreloadedBlock = {
*/
export function preloadBlocks(
blocks: registry.RemoteBlock[],
config: Pick<ProjectConfig, 'includeTests'>
config: Pick<ProjectConfig, 'includeTests' | 'includeDocs'>
): PreloadedBlock[] {
const preloaded: PreloadedBlock[] = [];

for (const block of blocks) {
// filters out test files if they are not supposed to be included
const includedFiles = block.files.filter((file) =>
isTestFile(file) ? config.includeTests : true
);
// filters out test/docs files if they are not supposed to be included
const includedFiles = block.files.filter((file) => {
if (isTestFile(file) && !config.includeTests) return false;

if (isDocsFile(file) && !config.includeDocs) return false;

return true;
});

const files = Promise.all(
includedFiles.map(async (file) => {
Expand Down
46 changes: 42 additions & 4 deletions src/utils/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import path from 'pathe';
import * as v from 'valibot';
import { type Block, type Category, type Manifest, categorySchema } from '../../types';
import * as ascii from '../ascii';
import * as strings from '../blocks/ts/strings';
import type { RegistryConfig } from '../config';
import { languages } from '../language-support';
import { isDependedOn } from './check';
Expand All @@ -25,8 +26,14 @@ const TEST_SUFFIXES = [
'_stories.tsx',
] as const;

const DOCS_SUFFIXES = ['.mdx', '.md'] as const;

export function isTestFile(file: string): boolean {
return TEST_SUFFIXES.find((suffix) => file.endsWith(suffix)) !== undefined;
return strings.endsWithOneOf(file, TEST_SUFFIXES) !== undefined;
}

export function isDocsFile(file: string): boolean {
return strings.endsWithOneOf(file, DOCS_SUFFIXES) !== undefined;
}

type Options = {
Expand Down Expand Up @@ -87,6 +94,16 @@ export function buildBlocksDirectory(
if (fs.statSync(blockDir).isFile()) {
if (isTestFile(file)) continue;

if (isDocsFile(file)) {
if (!config.includeDocs) {
console.warn(
`${ascii.VERTICAL_LINE} ${ascii.WARN} Documentation files (*.md, *.mdx) are not included by default include them with ${color.bold('--include-docs')}!`
);
}

continue;
}

const name = transformBlockName(file);

const listBlock = shouldListBlock(name, config);
Expand All @@ -111,6 +128,11 @@ export function buildBlocksDirectory(
TEST_SUFFIXES.find((suffix) => f === `${name}${suffix}`)
);

// tries to find a docs file with the same name as the file
const docsPath = files.find((f) =>
DOCS_SUFFIXES.find((suffix) => f === `${name}${suffix}`)
);

const { dependencies, devDependencies, local, imports } = lang
.resolveDependencies({
filePath: blockDir,
Expand All @@ -131,6 +153,7 @@ export function buildBlocksDirectory(
directory: path.relative(cwd, categoryDir),
category: categoryName,
tests: testsPath !== undefined,
docs: docsPath !== undefined,
subdirectory: false,
list: listCategory ? listBlock : false,
files: [file],
Expand All @@ -141,9 +164,10 @@ export function buildBlocksDirectory(
};

// if test file exists add the file
if (testsPath !== undefined) {
block.files.push(testsPath);
}
if (testsPath !== undefined) block.files.push(testsPath);

// if docs file exists add the file
if (docsPath !== undefined) block.files.push(docsPath);

category.blocks.push(block);
} else {
Expand All @@ -159,6 +183,7 @@ export function buildBlocksDirectory(
const imports: Record<string, string> = {};

let hasTests = false;
let hasDocs = false;

const blockFiles: string[] = [];

Expand All @@ -176,6 +201,18 @@ export function buildBlocksDirectory(
continue;
}

if (isDocsFile(f)) {
if (!config.includeDocs) {
console.warn(
`${ascii.VERTICAL_LINE} ${ascii.WARN} Documentation files (*.md, *.mdx) are not included by default include them with ${color.bold('--include-docs')}!`
);
}

hasDocs = true;
blockFiles.push(relativeFilePath);
continue;
}

if (fs.statSync(filePath).isDirectory()) {
if (!config.allowSubdirectories) {
console.warn(
Expand Down Expand Up @@ -253,6 +290,7 @@ export function buildBlocksDirectory(
directory: path.relative(cwd, blockDir),
category: categoryName,
tests: hasTests,
docs: hasDocs,
subdirectory: true,
list: listCategory ? listBlock : false,
files: blockFiles,
Expand Down
2 changes: 2 additions & 0 deletions src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const projectConfigSchema = v.object({
$schema: v.string(),
repos: v.optional(v.array(v.string()), []),
includeTests: v.boolean(),
includeDocs: v.optional(v.boolean(), false),
paths: pathsSchema,
configFiles: v.optional(v.record(v.string(), v.string())),
watermark: v.optional(v.boolean(), true),
Expand Down Expand Up @@ -85,6 +86,7 @@ export const registryConfigSchema = v.object({
excludeDeps: v.optional(v.array(v.string()), []),
allowSubdirectories: v.optional(v.boolean()),
preview: v.optional(v.boolean()),
includeDocs: v.optional(v.boolean(), false),
rules: v.optional(ruleConfigSchema),
});

Expand Down
14 changes: 12 additions & 2 deletions src/utils/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ const getComponentCodeTool: Tool = {
description: 'Should tests be included with the component code.',
default: false,
},
includeDocs: {
type: 'boolean',
description: 'Should docs be included with the component code.',
default: false,
},
},
required: ['component'],
},
Expand All @@ -111,9 +116,14 @@ const getComponentCodeTool: Tool = {
interface GetComponentCodeArgs {
component: string;
includeTests?: boolean;
includeDocs?: boolean;
}

async function getComponentCode({ component, includeTests = false }: GetComponentCodeArgs) {
async function getComponentCode({
component,
includeTests = false,
includeDocs = false,
}: GetComponentCodeArgs) {
const provider = registry.selectProvider(component);

if (!provider) {
Expand Down Expand Up @@ -152,7 +162,7 @@ async function getComponentCode({ component, includeTests = false }: GetComponen
throw new Error(`${specifier} does not exist in ${repo}`);
}

const preloaded = preloadBlocks([block], { includeTests });
const preloaded = preloadBlocks([block], { includeTests, includeDocs });

const files = (await Promise.all(preloaded.map((p) => p.files))).flatMap((p) => [
...p.map((f) => ({ name: f.name, content: f.content.unwrapOr('<FETCH ERROR>') })),
Expand Down
Loading