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
20 changes: 20 additions & 0 deletions .vitepress/theme/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,23 @@
width: 12px;
height: 12px;
}

.atom-link {
display: inline-flex;
align-items: center;
vertical-align: middle;
color: var(--vp-c-text-2);
transition: color 0.2s;
text-decoration: none !important;
margin-left: 12px;
}

.atom-link:hover {
color: var(--vp-c-orange) !important;
}

.atom-icon {
width: 24px;
height: 24px;
fill: currentColor;
}
3 changes: 3 additions & 0 deletions .vitepress/theme/variables.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
--cm-selection-bg-dark: #3e4451;
--cm-selection-bg-light: #d7d4f0;

/* Atom Icon */
--vp-c-orange: #f26522;

/* Modal & Overlays */
--overlay-bg: rgba(0, 0, 0, 0.5);
--modal-shadow: rgba(0, 0, 0, 0.3);
Expand Down
52 changes: 51 additions & 1 deletion cli/generateChangelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import fs from 'node:fs/promises';
import path from 'node:path';

import { Semaphore } from 'es-toolkit';
import { Feed } from 'feed';
import Semver from 'semver';

import { getMilestoneIssues, getMilestones } from './gh.js';
import { getExisting, getModuleChangelogPath, getModuleMarkdownChangelogPath } from './paths.js';
import { modules } from './modules.js';
import { getExisting, getModuleChangelogPath, getModuleMarkdownChangelogPath, PUBLIC_ATOM_DIR } from './paths.js';

import type { ChangelogItem } from './types.js';

Expand Down Expand Up @@ -65,6 +67,54 @@ export const generateChangelog = async (moduleName: string) => {
}

await generateModuleMarkdownChangelog(moduleName, sortedChangelog);
await generateModuleAtom(moduleName, sortedChangelog);
};

const generateModuleAtom = async (moduleName: string, sortedChangelog: ChangelogItem[]) => {
const isJoi = moduleName === 'joi';
const spec = modules[moduleName];
const fullModuleName = spec?.package ?? moduleName;
const baseLink = isJoi ? 'https://joi.dev' : `https://joi.dev/module/${moduleName}`;
const changelogLink = isJoi
? 'https://joi.dev/resources/changelog'
: `https://joi.dev/module/${moduleName}/changelog`;

const feed = new Feed({
favicon: 'https://joi.dev/favicon.png',
id: baseLink,
image: 'https://joi.dev/img/logo.png',
language: 'en',
link: baseLink,
title: `${fullModuleName} changelog`,
updated: new Date(sortedChangelog[0]?.date ?? Date.now()),
});

const items = sortedChangelog.slice(0, 50);

for (const item of items) {
const title = `${fullModuleName} v${item.version}`;
const link = `${changelogLink}#${item.version}`;
const date = new Date(item.date);

let content = '<ul>';
for (const issue of item.issues) {
content += `<li><a href="${issue.url}">[#${issue.number}]</a> ${escapeHtml(issue.title)}</li>`;
}
content += '</ul>';

feed.addItem({
content,
date,
description: title,
id: item.url,
link,
title,
});
}

const atomPath = path.join(PUBLIC_ATOM_DIR, `${moduleName}.atom`);
await fs.mkdir(path.dirname(atomPath), { recursive: true });
await fs.writeFile(atomPath, feed.atom1());
};

const generateModuleMarkdownChangelog = async (moduleName: string, sortedChangelog: ChangelogItem[]) => {
Expand Down
29 changes: 29 additions & 0 deletions cli/getModuleInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
API_DIR,
METADATA_DIR,
MODULE_DIR,
PACKAGE_JSON_PATH,
POLICIES_GENERATED_DIR,
getExisting,
getModuleInfoPath,
Expand Down Expand Up @@ -226,6 +227,34 @@ await Promise.all(
await fs.mkdir(METADATA_DIR, { recursive: true });
await fs.writeFile(path.join(METADATA_DIR, 'modules.json'), JSON.stringify(sortedRepos, null, 2));

console.info('Updating joi dependencies...');
const packageJson = JSON.parse(await fs.readFile(PACKAGE_JSON_PATH, 'utf8'));
const joiMajors = Object.keys(modules.joi.compatibility);
const joiRepo = repos.joi;
let changed = false;

for (const majorStr of joiMajors) {
const major = parseInt(majorStr, 10);
const depName = `joi-${major}`;
const latestVersion = joiRepo?.versionsArray?.find((v) => Semver.major(v) === major);

if (latestVersion) {
const depValue = `npm:joi@${latestVersion}`;
if (packageJson.dependencies[depName] !== depValue) {
packageJson.dependencies[depName] = depValue;
changed = true;
}
} else {
console.warn(`Could not find latest version for joi major ${major}`);
}
}

if (changed) {
await fs.writeFile(PACKAGE_JSON_PATH, `${JSON.stringify(packageJson, null, 2)}\n`);
console.info('Running pnpm install...');
execFileSync('pnpm', ['install'], { stdio: 'inherit' });
}

// Generate module/index.md
const moduleIndexMdPath = path.join(MODULE_DIR, 'index.md');
const moduleIndexContent = `# Modules
Expand Down
5 changes: 5 additions & 0 deletions cli/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export const MARKDOWN_DIR = path.join(GENERATED_DIR, 'markdown');
export const POLICIES_GENERATED_DIR = path.join(MARKDOWN_DIR, 'policies');
export const METADATA_DIR = path.join(GENERATED_DIR, 'metadata');
export const MODULES_DIR = path.join(GENERATED_DIR, 'modules');
export const ROOT_DIR = path.join(import.meta.dirname, '..');
export const PACKAGE_JSON_PATH = path.join(ROOT_DIR, 'package.json');
export const PUBLIC_ATOM_DIR = path.join(import.meta.dirname, '../docs/public/atom');

export const getModuleMarkdownPath = (moduleName: string, major: string | number) =>
path.join(MARKDOWN_DIR, moduleName, major.toString(), 'api.md');
Expand All @@ -22,6 +25,8 @@ export const getModuleInfoPath = (moduleName: string) => path.join(getModuleStor
export const getModuleChangelogPath = (moduleName: string) =>
path.join(getModuleStoragePath(moduleName), 'changelog.json');

export const getModuleAtomPath = (moduleName: string) => path.join(getModuleStoragePath(moduleName), 'changelog.atom');

export const getExisting = async <T>(filePath: string): Promise<T | undefined> => {
try {
const content = await fs.readFile(filePath, 'utf8');
Expand Down
3 changes: 0 additions & 3 deletions components/ApiOutline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { onMounted, ref } from 'vue';

const outline = ref(null);


const onActiveChange = (mutations) => {
for (const { target, type, attributeName } of mutations) {
if (
Expand All @@ -18,12 +17,10 @@ const onActiveChange = (mutations) => {
}
};


onMounted(() => {
outline.value = document.querySelector('.VPDocAsideOutline');
});


useMutationObserver(outline, onActiveChange, {
attributeFilter: ['class'],
subtree: true,
Expand Down
4 changes: 0 additions & 4 deletions components/CarbonAds.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,23 @@ import { onMounted, watch } from 'vue';

const route = useRoute();


const load = () => {
const script = document.createElement('script');
script.id = '_carbonads_js';
script.src = `//cdn.carbonads.com/carbon.js?serve=CEAIL27W&placement=joidev`;
script.async = true;


const container = document.querySelector('#carbon-ad');
if (container) {
container.innerHTML = '';
container.append(script);
}
};


onMounted(() => {
load();
});


watch(
() => route.path,
() => {
Expand Down
17 changes: 0 additions & 17 deletions components/CodeMirrorEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,16 @@
readOnly: { default: false, type: Boolean },
});


const data = defineModel({ default: '', type: String });


const { isDark } = useData();
const editorContainer = ref(null);
let view = null;


const themeCompartment = new Compartment();
const joiCompartment = new Compartment();
const setErrorLines = StateEffect.define();


const errorLineField = StateField.define({
create() {
return Decoration.none;
Expand All @@ -62,7 +58,6 @@
},
});


const getThemeExtension = () => {
const theme = isDark.value ? darcula : eclipse;
const cursorTheme = EditorView.theme({
Expand All @@ -76,7 +71,6 @@
return [theme, cursorTheme];
};


const getJoiExtension = () => {
if (!joiVersion) {
return [];
Expand All @@ -86,13 +80,11 @@
});
};


const format = async () => {
if (!view) {
return;
}


const code = view.state.doc.toString();
try {
const [prettier, prettierPluginBabel, prettierPluginEstree] = await Promise.all([
Expand All @@ -101,7 +93,6 @@
import('prettier/plugins/estree'),
]);


const formatted = await prettier.format(code, {
parser: language === 'json' ? 'json' : 'babel',
plugins: [
Expand All @@ -114,21 +105,18 @@
trailingComma: 'all',
});


if (formatted !== code) {
view.dispatch({
changes: { from: 0, insert: formatted, to: view.state.doc.length },
});
}
} catch (err) {

Check warning on line 113 in components/CodeMirrorEditor.vue

View workflow job for this annotation

GitHub Actions / test

eslint-plugin-unicorn(catch-error-name)

The catch parameter "err" should be named "error"
console.error('Format error:', err);
}
};


defineExpose({ format });

Check warning on line 118 in components/CodeMirrorEditor.vue

View workflow job for this annotation

GitHub Actions / test

eslint(no-undef)

'defineExpose' is not defined.


onMounted(() => {
const extensions = [
basicSetup,
Expand Down Expand Up @@ -162,7 +150,6 @@
}
});


watch(data, (newValue) => {
if (view && newValue !== view.state.doc.toString()) {
view.dispatch({
Expand All @@ -171,7 +158,6 @@
}
});


watch(
() => errorLines,
(newLines) => {
Expand All @@ -184,7 +170,6 @@
{ deep: true },
);


watch(isDark, () => {
if (view) {
view.dispatch({
Expand All @@ -193,7 +178,6 @@
}
});


watch(
() => joiVersion,
() => {
Expand All @@ -205,7 +189,6 @@
},
);


onBeforeUnmount(() => {
if (view) {
view.destroy();
Expand Down
1 change: 0 additions & 1 deletion components/ModuleIndex.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ const search = ref('');
const sort = ref('name');
const moduleInfo = ref(modulesData);


const filteredModules = computed(() => {
const searchTerm = search.value.trim().toLowerCase();
const modules = Object.entries(moduleInfo.value)
Expand Down
1 change: 0 additions & 1 deletion components/StatusContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ import modulesData from '../generated/metadata/modules.json' with { type: 'json'

const modules = ref(modulesData);


const filteredModules = computed(() => modules.value);
</script>

Expand Down
Loading