@olwiba/dx 0.0.9

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.
@@ -0,0 +1,9 @@
1
+ import { Linter } from 'eslint';
2
+
3
+ type OlwibaEslintOptions = {
4
+ react?: boolean;
5
+ files?: string[];
6
+ };
7
+ declare function olwiba(options?: OlwibaEslintOptions): Promise<Linter.Config[]>;
8
+
9
+ export { type OlwibaEslintOptions, olwiba };
package/dist/eslint.js ADDED
@@ -0,0 +1,90 @@
1
+ // src/rules/no-default-export.ts
2
+ var noDefaultExport = {
3
+ meta: {
4
+ type: "suggestion",
5
+ docs: {
6
+ description: "Disallow default exports \u2014 use named exports"
7
+ },
8
+ messages: {
9
+ noDefault: "Use named exports. Default exports make refactoring harder."
10
+ },
11
+ schema: []
12
+ },
13
+ create(context) {
14
+ return {
15
+ ExportDefaultDeclaration(node) {
16
+ context.report({ node, messageId: "noDefault" });
17
+ }
18
+ };
19
+ }
20
+ };
21
+
22
+ // src/eslint.ts
23
+ var olwibaPlugin = {
24
+ name: "@olwiba/dx",
25
+ rules: { "no-default-export": noDefaultExport }
26
+ };
27
+ var lazyImport = (id) => import(id);
28
+ function getDefault(mod) {
29
+ if (typeof mod === "object" && mod !== null && "default" in mod) {
30
+ return mod.default;
31
+ }
32
+ return mod;
33
+ }
34
+ async function olwiba(options = {}) {
35
+ const { react = false, files = ["**/*.{ts,tsx,js,jsx,mts,cts}"] } = options;
36
+ const configs = [];
37
+ configs.push({
38
+ files,
39
+ plugins: { "@olwiba/dx": olwibaPlugin },
40
+ rules: {
41
+ "@olwiba/dx/no-default-export": "error",
42
+ "no-console": "warn"
43
+ }
44
+ });
45
+ try {
46
+ const [tsPluginMod, tsParserMod] = await Promise.all([
47
+ lazyImport("@typescript-eslint/eslint-plugin"),
48
+ lazyImport("@typescript-eslint/parser")
49
+ ]);
50
+ configs.push({
51
+ files: ["**/*.{ts,tsx,mts,cts}"],
52
+ languageOptions: { parser: getDefault(tsParserMod) },
53
+ plugins: { "@typescript-eslint": getDefault(tsPluginMod) },
54
+ rules: {
55
+ "@typescript-eslint/no-explicit-any": "error",
56
+ "@typescript-eslint/consistent-type-imports": [
57
+ "error",
58
+ { prefer: "type-imports", fixStyle: "separate-type-imports" }
59
+ ],
60
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }]
61
+ }
62
+ });
63
+ } catch {
64
+ process.stderr.write(
65
+ "[olwiba/dx] tip: install @typescript-eslint/eslint-plugin + @typescript-eslint/parser for TypeScript rules\n"
66
+ );
67
+ }
68
+ if (react) {
69
+ try {
70
+ const reactHooksMod = await lazyImport("eslint-plugin-react-hooks");
71
+ configs.push({
72
+ files: ["**/*.{tsx,jsx}"],
73
+ plugins: { "react-hooks": getDefault(reactHooksMod) },
74
+ rules: {
75
+ "react-hooks/rules-of-hooks": "error",
76
+ "react-hooks/exhaustive-deps": "warn"
77
+ }
78
+ });
79
+ } catch {
80
+ process.stderr.write(
81
+ "[olwiba/dx] tip: install eslint-plugin-react-hooks for React rules\n"
82
+ );
83
+ }
84
+ }
85
+ return configs;
86
+ }
87
+
88
+ export { olwiba };
89
+ //# sourceMappingURL=eslint.js.map
90
+ //# sourceMappingURL=eslint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rules/no-default-export.ts","../src/eslint.ts"],"names":[],"mappings":"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","file":"eslint.js","sourcesContent":["import type { Rule } from \"eslint\"\n\nexport const noDefaultExport: Rule.RuleModule = {\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"Disallow default exports — use named exports\",\n },\n messages: {\n noDefault: \"Use named exports. Default exports make refactoring harder.\",\n },\n schema: [],\n },\n create(context) {\n return {\n ExportDefaultDeclaration(node) {\n context.report({ node, messageId: \"noDefault\" })\n },\n }\n },\n}\n","import type { ESLint, Linter, Rule } from \"eslint\"\nimport { noDefaultExport } from \"./rules/no-default-export\"\n\nconst olwibaPlugin = {\n name: \"@olwiba/dx\",\n rules: { \"no-default-export\": noDefaultExport } satisfies Record<string, Rule.RuleModule>,\n}\n\nexport type OlwibaEslintOptions = {\n react?: boolean\n files?: string[]\n}\n\n// Wrapping import() in a function makes TypeScript return Promise<any>,\n// bypassing module resolution for optional peer deps.\nconst lazyImport = (id: string) => import(id)\n\nfunction getDefault<T>(mod: unknown): T {\n if (typeof mod === \"object\" && mod !== null && \"default\" in mod) {\n return (mod as { default: T }).default\n }\n return mod as T\n}\n\nexport async function olwiba(options: OlwibaEslintOptions = {}): Promise<Linter.Config[]> {\n const { react = false, files = [\"**/*.{ts,tsx,js,jsx,mts,cts}\"] } = options\n const configs: Linter.Config[] = []\n\n configs.push({\n files,\n plugins: { \"@olwiba/dx\": olwibaPlugin },\n rules: {\n \"@olwiba/dx/no-default-export\": \"error\",\n \"no-console\": \"warn\",\n },\n })\n\n try {\n const [tsPluginMod, tsParserMod] = await Promise.all([\n lazyImport(\"@typescript-eslint/eslint-plugin\"),\n lazyImport(\"@typescript-eslint/parser\"),\n ])\n configs.push({\n files: [\"**/*.{ts,tsx,mts,cts}\"],\n languageOptions: { parser: getDefault<Linter.Parser>(tsParserMod) },\n plugins: { \"@typescript-eslint\": getDefault<ESLint.Plugin>(tsPluginMod) },\n rules: {\n \"@typescript-eslint/no-explicit-any\": \"error\",\n \"@typescript-eslint/consistent-type-imports\": [\n \"error\",\n { prefer: \"type-imports\", fixStyle: \"separate-type-imports\" },\n ],\n \"@typescript-eslint/no-unused-vars\": [\"error\", { argsIgnorePattern: \"^_\" }],\n },\n })\n } catch {\n process.stderr.write(\n \"[olwiba/dx] tip: install @typescript-eslint/eslint-plugin + @typescript-eslint/parser for TypeScript rules\\n\"\n )\n }\n\n if (react) {\n try {\n const reactHooksMod = await lazyImport(\"eslint-plugin-react-hooks\")\n configs.push({\n files: [\"**/*.{tsx,jsx}\"],\n plugins: { \"react-hooks\": getDefault<ESLint.Plugin>(reactHooksMod) },\n rules: {\n \"react-hooks/rules-of-hooks\": \"error\",\n \"react-hooks/exhaustive-deps\": \"warn\",\n },\n })\n } catch {\n process.stderr.write(\n \"[olwiba/dx] tip: install eslint-plugin-react-hooks for React rules\\n\"\n )\n }\n }\n\n return configs\n}\n"]}
@@ -0,0 +1,7 @@
1
+ declare const asciiFonts: {
2
+ readonly dosrebel: string;
3
+ };
4
+ type AsciiFontName = keyof typeof asciiFonts;
5
+ declare function getAsciiFont(name: AsciiFontName): string;
6
+
7
+ export { type AsciiFontName as A, asciiFonts as a, getAsciiFont as g };
@@ -0,0 +1,28 @@
1
+ interface PreviewEntry {
2
+ name: string;
3
+ urlPath?: string;
4
+ }
5
+ interface GeneratePreviewsConfig {
6
+ baseUrl: string;
7
+ outputDir: string;
8
+ components: PreviewEntry[];
9
+ selector?: string | null;
10
+ themes?: ('light' | 'dark')[];
11
+ manifestPath?: string;
12
+ executablePath?: string;
13
+ padding?: number;
14
+ viewport?: {
15
+ width?: number;
16
+ height?: number;
17
+ };
18
+ }
19
+ interface ManifestEntry {
20
+ name: string;
21
+ theme: 'light' | 'dark';
22
+ file: string;
23
+ width: number;
24
+ height: number;
25
+ }
26
+ declare function generatePreviews(config: GeneratePreviewsConfig): Promise<void>;
27
+
28
+ export { type GeneratePreviewsConfig, type ManifestEntry, type PreviewEntry, generatePreviews };
@@ -0,0 +1,130 @@
1
+ import puppeteer from 'puppeteer-core';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ // src/generate-previews.ts
6
+ function findChromePath() {
7
+ const candidates = process.platform === "win32" ? [
8
+ "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
9
+ "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
10
+ `${process.env.LOCALAPPDATA}\\Google\\Chrome\\Application\\chrome.exe`,
11
+ "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
12
+ `${process.env.ProgramFiles}\\Microsoft\\Edge\\Application\\msedge.exe`
13
+ ] : process.platform === "darwin" ? [
14
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
15
+ "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
16
+ ] : ["/usr/bin/google-chrome", "/usr/bin/chromium-browser", "/usr/bin/chromium"];
17
+ for (const p of candidates) {
18
+ if (p && fs.existsSync(p)) return p;
19
+ }
20
+ throw new Error(
21
+ "No Chrome/Edge executable found. Pass executablePath in config or install Chrome."
22
+ );
23
+ }
24
+ async function generatePreviews(config) {
25
+ const {
26
+ baseUrl,
27
+ outputDir,
28
+ components,
29
+ selector = '[data-slot="component-preview-canvas"]',
30
+ themes = ["light", "dark"],
31
+ manifestPath,
32
+ executablePath,
33
+ padding = 0,
34
+ viewport
35
+ } = config;
36
+ const vw = viewport?.width ?? 1280;
37
+ const vh = viewport?.height ?? 800;
38
+ try {
39
+ const res = await fetch(baseUrl);
40
+ if (!res.ok) throw new Error();
41
+ } catch {
42
+ console.error(`
43
+ Dev server not running at ${baseUrl}.
44
+ Start it first, then re-run iso:generate.
45
+ `);
46
+ process.exit(1);
47
+ }
48
+ const chromePath = executablePath ?? findChromePath();
49
+ console.log(`Using browser: ${chromePath}`);
50
+ fs.mkdirSync(outputDir, { recursive: true });
51
+ const browser = await puppeteer.launch({
52
+ executablePath: chromePath,
53
+ headless: true,
54
+ args: ["--no-sandbox", "--disable-setuid-sandbox"]
55
+ });
56
+ const manifest = [];
57
+ try {
58
+ for (const component of components) {
59
+ const urlPath = component.urlPath ?? `/docs/components/${component.name}`;
60
+ const url = `${baseUrl}${urlPath}`;
61
+ for (const theme of themes) {
62
+ const page = await browser.newPage();
63
+ try {
64
+ await page.setViewport({ width: vw, height: vh });
65
+ await page.goto(url, { waitUntil: "networkidle0" });
66
+ if (theme === "dark") {
67
+ await page.evaluate("document.documentElement.classList.add('dark')");
68
+ await new Promise((r) => setTimeout(r, 150));
69
+ }
70
+ const filename = themes.length > 1 ? `${component.name}-${theme}.png` : `${component.name}.png`;
71
+ const filepath = path.join(outputDir, filename);
72
+ if (selector === null) {
73
+ await page.screenshot({ path: filepath, clip: { x: 0, y: 0, width: vw, height: vh } });
74
+ manifest.push({ name: component.name, theme, file: filename, width: vw, height: vh });
75
+ } else {
76
+ const el = await page.waitForSelector(selector, { timeout: 1e4 });
77
+ if (!el) throw new Error(`Selector not found: ${selector}`);
78
+ await page.evaluate(
79
+ `document.querySelector(${JSON.stringify(selector)}).style.minHeight = '0'`
80
+ );
81
+ await new Promise((r) => setTimeout(r, 100));
82
+ const box = await el.boundingBox();
83
+ if (!box) throw new Error(`Could not get bounding box for: ${selector}`);
84
+ if (padding > 0) {
85
+ const clip = {
86
+ x: Math.max(0, box.x - padding),
87
+ y: Math.max(0, box.y - padding),
88
+ width: box.width + padding * 2,
89
+ height: box.height + padding * 2
90
+ };
91
+ await page.screenshot({ path: filepath, clip });
92
+ manifest.push({
93
+ name: component.name,
94
+ theme,
95
+ file: filename,
96
+ width: Math.round(clip.width),
97
+ height: Math.round(clip.height)
98
+ });
99
+ } else {
100
+ await el.screenshot({ path: filepath });
101
+ manifest.push({
102
+ name: component.name,
103
+ theme,
104
+ file: filename,
105
+ width: Math.round(box.width),
106
+ height: Math.round(box.height)
107
+ });
108
+ }
109
+ }
110
+ console.log(`\u2713 ${filename}`);
111
+ } catch (err) {
112
+ console.error(`\u2717 ${component.name} (${theme}):`, err);
113
+ } finally {
114
+ await page.close();
115
+ }
116
+ }
117
+ }
118
+ } finally {
119
+ await browser.close();
120
+ }
121
+ const resolvedManifestPath = manifestPath ?? path.join(outputDir, "manifest.json");
122
+ fs.mkdirSync(path.dirname(resolvedManifestPath), { recursive: true });
123
+ fs.writeFileSync(resolvedManifestPath, JSON.stringify(manifest, null, 2));
124
+ console.log(`
125
+ Generated ${manifest.length} previews \u2192 ${outputDir}`);
126
+ }
127
+
128
+ export { generatePreviews };
129
+ //# sourceMappingURL=generate-previews.js.map
130
+ //# sourceMappingURL=generate-previews.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/generate-previews.ts"],"names":[],"mappings":"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","file":"generate-previews.js","sourcesContent":["import puppeteer from 'puppeteer-core';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nexport interface PreviewEntry {\n name: string;\n urlPath?: string;\n}\n\nexport interface GeneratePreviewsConfig {\n baseUrl: string;\n outputDir: string;\n components: PreviewEntry[];\n selector?: string | null;\n themes?: ('light' | 'dark')[];\n manifestPath?: string;\n executablePath?: string;\n padding?: number;\n viewport?: { width?: number; height?: number };\n}\n\nexport interface ManifestEntry {\n name: string;\n theme: 'light' | 'dark';\n file: string;\n width: number;\n height: number;\n}\n\nfunction findChromePath(): string {\n const candidates =\n process.platform === 'win32'\n ? [\n 'C:\\\\Program Files\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe',\n 'C:\\\\Program Files (x86)\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe',\n `${process.env.LOCALAPPDATA}\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe`,\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe',\n `${process.env.ProgramFiles}\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe`,\n ]\n : process.platform === 'darwin'\n ? [\n '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',\n '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',\n ]\n : ['/usr/bin/google-chrome', '/usr/bin/chromium-browser', '/usr/bin/chromium'];\n\n for (const p of candidates) {\n if (p && fs.existsSync(p)) return p;\n }\n throw new Error(\n 'No Chrome/Edge executable found. Pass executablePath in config or install Chrome.',\n );\n}\n\nexport async function generatePreviews(config: GeneratePreviewsConfig): Promise<void> {\n const {\n baseUrl,\n outputDir,\n components,\n selector = '[data-slot=\"component-preview-canvas\"]',\n themes = ['light', 'dark'],\n manifestPath,\n executablePath,\n padding = 0,\n viewport,\n } = config;\n\n const vw = viewport?.width ?? 1280;\n const vh = viewport?.height ?? 800;\n\n try {\n const res = await fetch(baseUrl);\n if (!res.ok) throw new Error();\n } catch {\n console.error(`\\nDev server not running at ${baseUrl}.\\nStart it first, then re-run iso:generate.\\n`);\n process.exit(1);\n }\n\n const chromePath = executablePath ?? findChromePath();\n console.log(`Using browser: ${chromePath}`);\n\n fs.mkdirSync(outputDir, { recursive: true });\n\n const browser = await puppeteer.launch({\n executablePath: chromePath,\n headless: true,\n args: ['--no-sandbox', '--disable-setuid-sandbox'],\n });\n\n const manifest: ManifestEntry[] = [];\n\n try {\n for (const component of components) {\n const urlPath = component.urlPath ?? `/docs/components/${component.name}`;\n const url = `${baseUrl}${urlPath}`;\n\n for (const theme of themes) {\n const page = await browser.newPage();\n try {\n await page.setViewport({ width: vw, height: vh });\n await page.goto(url, { waitUntil: 'networkidle0' });\n\n if (theme === 'dark') {\n await page.evaluate(\"document.documentElement.classList.add('dark')\");\n await new Promise((r) => setTimeout(r, 150));\n }\n\n const filename =\n themes.length > 1 ? `${component.name}-${theme}.png` : `${component.name}.png`;\n const filepath = path.join(outputDir, filename);\n\n if (selector === null) {\n await page.screenshot({ path: filepath, clip: { x: 0, y: 0, width: vw, height: vh } });\n manifest.push({ name: component.name, theme, file: filename, width: vw, height: vh });\n } else {\n const el = await page.waitForSelector(selector, { timeout: 10_000 });\n if (!el) throw new Error(`Selector not found: ${selector}`);\n\n await page.evaluate(\n `document.querySelector(${JSON.stringify(selector)}).style.minHeight = '0'`,\n );\n await new Promise((r) => setTimeout(r, 100));\n\n const box = await el.boundingBox();\n if (!box) throw new Error(`Could not get bounding box for: ${selector}`);\n\n if (padding > 0) {\n const clip = {\n x: Math.max(0, box.x - padding),\n y: Math.max(0, box.y - padding),\n width: box.width + padding * 2,\n height: box.height + padding * 2,\n };\n await page.screenshot({ path: filepath, clip });\n manifest.push({\n name: component.name,\n theme,\n file: filename,\n width: Math.round(clip.width),\n height: Math.round(clip.height),\n });\n } else {\n await el.screenshot({ path: filepath });\n manifest.push({\n name: component.name,\n theme,\n file: filename,\n width: Math.round(box.width),\n height: Math.round(box.height),\n });\n }\n }\n\n console.log(`✓ ${filename}`);\n } catch (err) {\n console.error(`✗ ${component.name} (${theme}):`, err);\n } finally {\n await page.close();\n }\n }\n }\n } finally {\n await browser.close();\n }\n\n const resolvedManifestPath = manifestPath ?? path.join(outputDir, 'manifest.json');\n fs.mkdirSync(path.dirname(resolvedManifestPath), { recursive: true });\n fs.writeFileSync(resolvedManifestPath, JSON.stringify(manifest, null, 2));\n\n console.log(`\\nGenerated ${manifest.length} previews → ${outputDir}`);\n}\n"]}
@@ -0,0 +1,50 @@
1
+ type BannerSegment = {
2
+ text: string;
3
+ colorHex?: string;
4
+ };
5
+ type BannerOptions = {
6
+ segments: BannerSegment[];
7
+ compactSegments?: BannerSegment[];
8
+ };
9
+ type LegacyBannerOptions = {
10
+ baseText: string;
11
+ suffix?: string;
12
+ colorHex?: string;
13
+ baseColorHex?: string;
14
+ };
15
+ type DevPlugin = {
16
+ name: string;
17
+ apply: "serve";
18
+ configureServer: (server: unknown) => void;
19
+ };
20
+ declare function printBanner(input: string | BannerSegment[] | BannerOptions): Promise<void>;
21
+ declare function createTsupBannerHook(project: string | BannerOptions | LegacyBannerOptions): () => Promise<void>;
22
+ declare function createDevBannerPlugin(project: string | BannerOptions | LegacyBannerOptions): DevPlugin;
23
+
24
+ type ProjectBrandAccent = {
25
+ hex: string;
26
+ lightOklch?: string;
27
+ darkOklch?: string;
28
+ };
29
+ type ProjectBannerSegment = {
30
+ text: string;
31
+ accent?: boolean;
32
+ colorHex?: string;
33
+ };
34
+ type ProjectConfig = {
35
+ id: string;
36
+ label: string;
37
+ brandAccent: ProjectBrandAccent;
38
+ banner: {
39
+ segments: readonly ProjectBannerSegment[];
40
+ compactSegments?: readonly ProjectBannerSegment[];
41
+ };
42
+ };
43
+ declare function createProjectDevBannerPlugin(project: ProjectConfig): {
44
+ name: string;
45
+ apply: "serve";
46
+ configureServer: (server: unknown) => void;
47
+ };
48
+ declare function createProjectTsupBannerHook(project: ProjectConfig): () => Promise<void>;
49
+
50
+ export { type BannerSegment, type ProjectBannerSegment, type ProjectBrandAccent, type ProjectConfig, createDevBannerPlugin, createProjectDevBannerPlugin, createProjectTsupBannerHook, createTsupBannerHook, printBanner };