Skip to content

Commit 9b8202e

Browse files
1weihoclaude
andauthored
refactor(core,cli): dedupe internal helpers and trim over-exported API (#180)
Route duplicated HTTP (readBody/json), slide-path resolution, the SLIDE_ID_RE pattern, and locale format/plural through a single source instead of re-implementing them per module. Un-export internal-only helpers that were never consumed outside their own file. https://claude.ai/code/session_01D9RGMYuTWbtEu3eikkDBNb Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4a58723 commit 9b8202e

9 files changed

Lines changed: 29 additions & 91 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@open-slide/core": patch
3+
"@open-slide/cli": patch
4+
---
5+
6+
Remove duplicated internal helpers (HTTP `readBody`/`json`, slide-path resolution, the `SLIDE_ID_RE` pattern, and locale `format`/`plural`) by routing them through a single source.

packages/cli/src/git.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ async function run(cmd: string, args: string[], cwd: string): Promise<RunResult>
2424
});
2525
}
2626

27-
export async function isGitAvailable(): Promise<boolean> {
27+
async function isGitAvailable(): Promise<boolean> {
2828
try {
2929
const res = await run('git', ['--version'], process.cwd());
3030
return res.code === 0;
@@ -33,7 +33,7 @@ export async function isGitAvailable(): Promise<boolean> {
3333
}
3434
}
3535

36-
export async function isInsideWorkTree(cwd: string): Promise<boolean> {
36+
async function isInsideWorkTree(cwd: string): Promise<boolean> {
3737
try {
3838
const res = await run('git', ['rev-parse', '--is-inside-work-tree'], cwd);
3939
return res.code === 0 && res.stdout.trim() === 'true';

packages/core/src/app/lib/design-presets.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const SERIF_GEORGIA = 'Georgia, "Times New Roman", serif';
77
const SERIF_TIMES = '"Times New Roman", Times, serif';
88
const MONO_SF = '"SF Mono", "JetBrains Mono", Menlo, monospace';
99

10-
export const designPresets: DesignSystem[] = [
10+
const designPresets: DesignSystem[] = [
1111
defaultDesign,
1212
{
1313
palette: { bg: '#0f1115', text: '#f5f3ee', accent: '#7cc4ff' },
Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
11
import config from 'virtual:open-slide/config';
22
import { en } from '../../locale/en';
3-
import type { Locale, Plural } from '../../locale/types';
3+
import type { Locale } from '../../locale/types';
44

55
const resolved: Locale = (config.locale as Locale | undefined) ?? en;
66

77
export function useLocale(): Locale {
88
return resolved;
99
}
1010

11-
export function format(template: string, vars: Record<string, string | number>): string {
12-
return template.replace(/\{(\w+)\}/g, (m, key) => {
13-
const v = vars[key];
14-
return v === undefined ? m : String(v);
15-
});
16-
}
17-
18-
export function plural(count: number, forms: Plural): string {
19-
return count === 1 ? forms.one : forms.other;
20-
}
11+
export { format, plural } from '../../locale/format';

packages/core/src/vite/current-plugin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import fs from 'node:fs/promises';
22
import path from 'node:path';
33
import type { Plugin, ViteDevServer } from 'vite';
4+
import { SLIDE_ID_RE } from '../editing/slide-ops.ts';
45

5-
const SLIDE_ID_RE = /^[a-z0-9_-]+$/i;
66
const TEXT_SNIPPET_MAX = 120;
77

88
export type CurrentPluginOptions = {

packages/core/src/vite/design-plugin.ts

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,10 @@
11
import fs from 'node:fs/promises';
2-
import type { ServerResponse } from 'node:http';
3-
import path from 'node:path';
42
import { parse as babelParse } from '@babel/parser';
5-
import type { Connect, Plugin, ViteDevServer } from 'vite';
3+
import type { Plugin, ViteDevServer } from 'vite';
64
import { type DesignSystem, defaultDesign } from '../app/lib/design.ts';
75
import type { AstNode } from '../editing/babel-walk.ts';
86
import { validateMutationRequest } from '../http/request-guard.ts';
9-
10-
const SLIDE_ID_RE = /^[a-z0-9_-]+$/i;
11-
12-
async function readBody(req: Connect.IncomingMessage): Promise<unknown> {
13-
return await new Promise((resolve, reject) => {
14-
const chunks: Buffer[] = [];
15-
req.on('data', (c: Buffer) => chunks.push(c));
16-
req.on('end', () => {
17-
const raw = Buffer.concat(chunks).toString('utf8');
18-
if (!raw) return resolve({});
19-
try {
20-
resolve(JSON.parse(raw));
21-
} catch (e) {
22-
reject(e);
23-
}
24-
});
25-
req.on('error', reject);
26-
});
27-
}
28-
29-
function json(res: ServerResponse, status: number, body: unknown) {
30-
res.statusCode = status;
31-
res.setHeader('content-type', 'application/json');
32-
res.end(JSON.stringify(body));
33-
}
34-
35-
function resolveSlidePath(userCwd: string, slidesDir: string, slideId: string): string | null {
36-
if (!SLIDE_ID_RE.test(slideId)) return null;
37-
const slidesRoot = path.resolve(userCwd, slidesDir);
38-
const full = path.resolve(slidesRoot, slideId, 'index.tsx');
39-
if (!full.startsWith(`${slidesRoot}${path.sep}`)) return null;
40-
return full;
41-
}
7+
import { json, readBody, resolveSlidePath } from './routes/context.ts';
428

439
function parseSource(source: string): AstNode | null {
4410
try {

packages/core/src/vite/loc-tags-plugin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export type LocTagsPluginOptions = {
6666
// plugins or virtual modules can pass through Windows-style paths.
6767
// Compare both sides in POSIX shape so the match doesn't depend on
6868
// which separator the caller happened to use.
69-
export function isSlideSourceFile(id: string, slidesRootPosix: string): boolean {
69+
function isSlideSourceFile(id: string, slidesRootPosix: string): boolean {
7070
const filePath = id.split(/[?#]/)[0].replace(/\\/g, '/');
7171
if (!filePath.startsWith(`${slidesRootPosix}/`)) return false;
7272
if (!filePath.endsWith('.tsx')) return false;

packages/core/src/vite/notes-plugin.ts

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
import fs from 'node:fs/promises';
2-
import type { ServerResponse } from 'node:http';
3-
import path from 'node:path';
42
import { parse as babelParse } from '@babel/parser';
53
import * as t from '@babel/types';
6-
import type { Connect, Plugin, ViteDevServer } from 'vite';
4+
import type { Plugin, ViteDevServer } from 'vite';
75
import { validateMutationRequest } from '../http/request-guard.ts';
8-
9-
const SLIDE_ID_RE = /^[a-z0-9_-]+$/i;
6+
import { json, readBody, resolveSlidePath } from './routes/context.ts';
107

118
type NotesBody = {
129
slideId?: string;
@@ -18,37 +15,6 @@ export type ApplyNotesEditResult =
1815
| { ok: true; source: string }
1916
| { ok: false; status: number; error: string };
2017

21-
async function readBody(req: Connect.IncomingMessage): Promise<unknown> {
22-
return await new Promise((resolve, reject) => {
23-
const chunks: Buffer[] = [];
24-
req.on('data', (c: Buffer) => chunks.push(c));
25-
req.on('end', () => {
26-
const raw = Buffer.concat(chunks).toString('utf8');
27-
if (!raw) return resolve({});
28-
try {
29-
resolve(JSON.parse(raw));
30-
} catch (e) {
31-
reject(e);
32-
}
33-
});
34-
req.on('error', reject);
35-
});
36-
}
37-
38-
function json(res: ServerResponse, status: number, body: unknown) {
39-
res.statusCode = status;
40-
res.setHeader('content-type', 'application/json');
41-
res.end(JSON.stringify(body));
42-
}
43-
44-
function resolveSlidePath(userCwd: string, slidesDir: string, slideId: string): string | null {
45-
if (!SLIDE_ID_RE.test(slideId)) return null;
46-
const slidesRoot = path.resolve(userCwd, slidesDir);
47-
const full = path.resolve(slidesRoot, slideId, 'index.tsx');
48-
if (!full.startsWith(slidesRoot + path.sep)) return null;
49-
return full;
50-
}
51-
5218
function parseSource(source: string): t.File | null {
5319
try {
5420
return babelParse(source, {

packages/core/src/vite/routes/context.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,18 @@ export function json(res: ServerResponse, status: number, body: unknown) {
5050
res.end(JSON.stringify(body));
5151
}
5252

53-
export function resolveSlideEntryPath(ctx: ApiContext, slideId: string): string | null {
53+
export function resolveSlidePath(
54+
userCwd: string,
55+
slidesDir: string,
56+
slideId: string,
57+
): string | null {
5458
if (!SLIDE_ID_RE.test(slideId)) return null;
55-
const full = path.resolve(ctx.slidesRoot, slideId, 'index.tsx');
56-
if (!full.startsWith(ctx.slidesRoot + path.sep)) return null;
59+
const slidesRoot = path.resolve(userCwd, slidesDir);
60+
const full = path.resolve(slidesRoot, slideId, 'index.tsx');
61+
if (!full.startsWith(slidesRoot + path.sep)) return null;
5762
return full;
5863
}
64+
65+
export function resolveSlideEntryPath(ctx: ApiContext, slideId: string): string | null {
66+
return resolveSlidePath(ctx.userCwd, ctx.slidesDir, slideId);
67+
}

0 commit comments

Comments
 (0)