mirror of
https://github.com/robonen/tools.git
synced 2026-03-20 02:44:45 +00:00
feat(monorepo): migrate vue packages and apply oxlint refactors
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
@import "tailwindcss";
|
||||
@source "../../../../web/vue/src/**/demo.vue";
|
||||
@source "../../../../vue/toolkit/src/**/demo.vue";
|
||||
@source "../../../../core/stdlib/src/**/demo.vue";
|
||||
@source "../../../../core/platform/src/**/demo.vue";
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
<script setup lang="ts">defineProps<{
|
||||
kind: string;
|
||||
size?: 'sm' | 'md';
|
||||
}>();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
<script setup lang="ts">const props = defineProps<{
|
||||
code: string;
|
||||
lang?: string;
|
||||
}>();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import type { Component } from 'vue';
|
||||
<script setup lang="ts">import type { Component } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
component: Component;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import type { MethodMeta } from '../../modules/extractor/types';
|
||||
<script setup lang="ts">import type { MethodMeta } from '../../modules/extractor/types';
|
||||
|
||||
defineProps<{
|
||||
methods: MethodMeta[];
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import type { ParamMeta } from '../../modules/extractor/types';
|
||||
<script setup lang="ts">import type { ParamMeta } from '../../modules/extractor/types';
|
||||
|
||||
defineProps<{
|
||||
params: ParamMeta[];
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import type { PropertyMeta } from '../../modules/extractor/types';
|
||||
<script setup lang="ts">import type { PropertyMeta } from '../../modules/extractor/types';
|
||||
|
||||
defineProps<{
|
||||
properties: PropertyMeta[];
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
const { searchItems } = useDocs();
|
||||
<script setup lang="ts">const { searchItems } = useDocs();
|
||||
|
||||
const isOpen = ref(false);
|
||||
const query = ref('');
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
<script setup lang="ts">defineProps<{
|
||||
label: string;
|
||||
variant?: 'since' | 'test' | 'demo' | 'wip';
|
||||
}>();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createHighlighter, type Highlighter } from 'shiki';
|
||||
import { createHighlighter } from 'shiki';
|
||||
import type { Highlighter } from 'shiki';
|
||||
|
||||
let highlighterPromise: Promise<Highlighter> | null = null;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
const { getPackages } = useDocs();
|
||||
<script setup lang="ts">const { getPackages } = useDocs();
|
||||
const packages = getPackages();
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { demos } from '#docs/demos';
|
||||
<script setup lang="ts">import { demos } from '#docs/demos';
|
||||
|
||||
const route = useRoute();
|
||||
const { getItem } = useDocs();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
const route = useRoute();
|
||||
<script setup lang="ts">const route = useRoute();
|
||||
const { getPackage } = useDocs();
|
||||
|
||||
const slug = computed(() => route.params.package as string);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
const { getPackages, getTotalItems } = useDocs();
|
||||
<script setup lang="ts">const { getPackages, getTotalItems } = useDocs();
|
||||
const packages = getPackages();
|
||||
const totalItems = getTotalItems();
|
||||
|
||||
|
||||
@@ -7,21 +7,8 @@
|
||||
|
||||
import { resolve, relative, dirname } from 'node:path';
|
||||
import { existsSync, readFileSync } from 'node:fs';
|
||||
import {
|
||||
Project,
|
||||
type SourceFile,
|
||||
type FunctionDeclaration,
|
||||
type ClassDeclaration,
|
||||
type InterfaceDeclaration,
|
||||
type TypeAliasDeclaration,
|
||||
type JSDoc,
|
||||
type JSDocTag,
|
||||
type MethodDeclaration,
|
||||
type PropertyDeclaration,
|
||||
type PropertySignature,
|
||||
SyntaxKind,
|
||||
type Node,
|
||||
} from 'ts-morph';
|
||||
import { Project } from 'ts-morph';
|
||||
import type { SourceFile, FunctionDeclaration, ClassDeclaration, InterfaceDeclaration, TypeAliasDeclaration, JSDoc, JSDocTag, MethodDeclaration, PropertyDeclaration, PropertySignature } from 'ts-morph';
|
||||
import type {
|
||||
DocsMetadata,
|
||||
PackageMeta,
|
||||
@@ -41,7 +28,7 @@ const ROOT = resolve(import.meta.dirname, '..', '..', '..');
|
||||
const PACKAGES: PackageConfig[] = [
|
||||
{ path: 'core/stdlib', slug: 'stdlib' },
|
||||
{ path: 'core/platform', slug: 'platform' },
|
||||
{ path: 'web/vue', slug: 'vue' },
|
||||
{ path: 'vue/toolkit', slug: 'vue' },
|
||||
{ path: 'configs/oxlint', slug: 'oxlint' },
|
||||
];
|
||||
|
||||
@@ -74,16 +61,6 @@ function getTagValue(tags: JSDocTag[], tagName: string): string {
|
||||
return comment?.trim() ?? '';
|
||||
}
|
||||
|
||||
function getTagValues(tags: JSDocTag[], tagName: string): string[] {
|
||||
return tags
|
||||
.filter(t => t.getTagName() === tagName)
|
||||
.map(t => {
|
||||
const comment = t.getCommentText();
|
||||
return comment?.trim() ?? '';
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function getDescription(jsdocs: JSDoc[], tags: JSDocTag[]): string {
|
||||
// Try @description tag first
|
||||
const descTag = getTagValue(tags, 'description');
|
||||
@@ -101,7 +78,7 @@ function getDescription(jsdocs: JSDoc[], tags: JSDocTag[]): string {
|
||||
function getExamples(tags: JSDocTag[]): string[] {
|
||||
return tags
|
||||
.filter(t => t.getTagName() === 'example')
|
||||
.map(t => {
|
||||
.map((t) => {
|
||||
const text = t.getCommentText()?.trim() ?? '';
|
||||
// Strip surrounding ```ts ... ``` if present
|
||||
return text.replace(/^```(?:ts|typescript)?\n?/, '').replace(/\n?```$/, '').trim();
|
||||
@@ -120,8 +97,7 @@ function extractParams(tags: JSDocTag[], node: FunctionDeclaration | MethodDecla
|
||||
const defaultValue = param.getInitializer()?.getText() ?? null;
|
||||
|
||||
// Find matching @param tag
|
||||
const paramTag = paramTags.find(t => {
|
||||
const text = t.getCommentText() ?? '';
|
||||
const paramTag = paramTags.find((t) => {
|
||||
const tagText = t.getText();
|
||||
return tagText.includes(name);
|
||||
});
|
||||
@@ -220,28 +196,6 @@ function hasTestFile(sourceFilePath: string): boolean {
|
||||
return existsSync(resolve(dir, 'index.test.ts'));
|
||||
}
|
||||
|
||||
function inferCategory(sourceFilePath: string, tags: JSDocTag[]): string {
|
||||
// Try @category tag first
|
||||
const categoryTag = getTagValue(tags, 'category');
|
||||
if (categoryTag) return categoryTag;
|
||||
|
||||
// Infer from directory structure
|
||||
const parts = sourceFilePath.split('/src/');
|
||||
if (parts[1]) {
|
||||
const segments = parts[1].split('/');
|
||||
// For patterns like: composables/browser/useIntervalFn/index.ts → "Browser"
|
||||
// For patterns like: arrays/cluster/index.ts → "Arrays"
|
||||
if (segments.length >= 2) {
|
||||
const category = segments.length >= 3 ? segments[1] : segments[0];
|
||||
if (category) {
|
||||
return category.charAt(0).toUpperCase() + category.slice(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 'General';
|
||||
}
|
||||
|
||||
// ── Extraction ─────────────────────────────────────────────────────────────
|
||||
|
||||
function extractFunction(fn: FunctionDeclaration, sourceFilePath: string, entryPoint: string): ItemMeta | null {
|
||||
@@ -254,7 +208,6 @@ function extractFunction(fn: FunctionDeclaration, sourceFilePath: string, entryP
|
||||
const jsdocs = fn.getJsDocs();
|
||||
const tags = getJsDocTags(jsdocs);
|
||||
const description = getDescription(jsdocs, tags);
|
||||
const category = inferCategory(sourceFilePath, tags);
|
||||
|
||||
// Get signature text without body
|
||||
const signatureText = fn.getOverloads().length > 0
|
||||
@@ -464,7 +417,8 @@ function collectExportedItems(
|
||||
entryPoint,
|
||||
};
|
||||
items.push(item);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
const item = extractFunction(fn, filePath, entryPoint);
|
||||
if (item) items.push(item);
|
||||
}
|
||||
@@ -479,7 +433,6 @@ function collectExportedItems(
|
||||
for (const iface of sourceFile.getInterfaces()) {
|
||||
if (!iface.isExported()) continue;
|
||||
// Skip internal interfaces (e.g. Options, Return types that are documented inline)
|
||||
const name = iface.getName();
|
||||
const jsdocs = iface.getJsDocs();
|
||||
const tags = getJsDocTags(jsdocs);
|
||||
const hasCategory = getTagValue(tags, 'category') !== '';
|
||||
@@ -537,7 +490,8 @@ function groupCoLocatedTypes(items: ItemMeta[]): ItemMeta[] {
|
||||
const existing = typesByDir.get(dir) ?? [];
|
||||
existing.push(item);
|
||||
typesByDir.set(dir, existing);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
const existing = primaryByDir.get(dir) ?? [];
|
||||
existing.push(item);
|
||||
primaryByDir.set(dir, existing);
|
||||
@@ -595,12 +549,14 @@ function extractPackage(config: PackageConfig): PackageMeta | null {
|
||||
const fullPath = resolve(pkgDir, srcPath);
|
||||
if (existsSync(fullPath)) {
|
||||
entryPoints.push({ subpath, filePath: fullPath });
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// Try index.ts in subdirectory
|
||||
const altPath = resolve(pkgDir, srcPath.replace(/\.ts$/, '/index.ts'));
|
||||
if (existsSync(altPath)) {
|
||||
entryPoints.push({ subpath, filePath: altPath });
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
console.warn(`[extractor] Entry point not found: ${fullPath} or ${altPath}`);
|
||||
}
|
||||
}
|
||||
@@ -639,7 +595,7 @@ function extractPackage(config: PackageConfig): PackageMeta | null {
|
||||
|
||||
// Deduplicate by name (overloaded functions may appear once already)
|
||||
const seen = new Set<string>();
|
||||
const uniqueItems = allItems.filter(item => {
|
||||
const uniqueItems = allItems.filter((item) => {
|
||||
const key = `${item.entryPoint}:${item.name}`;
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
|
||||
@@ -27,10 +27,10 @@ export default defineNuxtModule({
|
||||
const metadata: DocsMetadata = extract();
|
||||
|
||||
// Add Vite resolve aliases for source packages so demo.vue imports resolve.
|
||||
// The web/vue package uses `@/` path aliases (e.g. `@/composables/...`).
|
||||
// The vue/toolkit package uses `@/` path aliases (e.g. `@/composables/...`).
|
||||
// We prepend them via vite:extendConfig so they take priority over Nuxt's
|
||||
// built-in `@` → srcDir alias.
|
||||
const vueSrc = resolve(ROOT, 'web/vue/src');
|
||||
const vueSrc = resolve(ROOT, 'vue/toolkit/src');
|
||||
|
||||
nuxt.hook('vite:extendConfig', (config) => {
|
||||
const existing = config.resolve?.alias;
|
||||
@@ -42,7 +42,8 @@ export default defineNuxtModule({
|
||||
|
||||
if (Array.isArray(existing)) {
|
||||
existing.unshift(...sourceAliases);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
config.resolve ??= {};
|
||||
config.resolve.alias = [
|
||||
...sourceAliases,
|
||||
|
||||
4
docs/oxlint.config.ts
Normal file
4
docs/oxlint.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { defineConfig } from 'oxlint';
|
||||
import { compose, base, imports, stylistic, typescript, vue } from '@robonen/oxlint';
|
||||
|
||||
export default defineConfig(compose(base, typescript, imports, vue, stylistic));
|
||||
@@ -6,6 +6,8 @@
|
||||
"description": "Auto-generated documentation site for @robonen/tools",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"lint:check": "oxlint -c oxlint.config.ts",
|
||||
"lint:fix": "oxlint -c oxlint.config.ts --fix",
|
||||
"dev": "nuxt dev",
|
||||
"build": "nuxt build",
|
||||
"generate": "nuxt generate",
|
||||
@@ -13,17 +15,20 @@
|
||||
"extract": "jiti ./modules/extractor/extract.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"shiki": "^3.4.2"
|
||||
"shiki": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/fonts": "^0.11.4",
|
||||
"@nuxt/fonts": "^0.14.0",
|
||||
"@nuxt/kit": "^4.3.1",
|
||||
"@tailwindcss/vite": "^4.1.0",
|
||||
"@robonen/oxlint": "workspace:*",
|
||||
"@stylistic/eslint-plugin": "catalog:",
|
||||
"@tailwindcss/vite": "^4.2.1",
|
||||
"jiti": "^2.6.1",
|
||||
"nuxt": "catalog:",
|
||||
"tailwindcss": "^4.1.0",
|
||||
"ts-morph": "^25.0.0",
|
||||
"oxlint": "catalog:",
|
||||
"tailwindcss": "^4.2.1",
|
||||
"ts-morph": "^27.0.2",
|
||||
"vue": "catalog:",
|
||||
"vue-router": "^4.5.1"
|
||||
"vue-router": "^5.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user