@mzebley/mark-down-cli 1.2.1 → 1.2.2
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.
- package/package.json +3 -2
- package/src/compile-page.ts +0 -116
- package/src/errors.ts +0 -9
- package/src/index.ts +0 -77
- package/src/logger.ts +0 -37
- package/src/manifest.ts +0 -155
- package/src/watch.ts +0 -64
- package/tsconfig.json +0 -9
- package/tsup.config.ts +0 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mzebley/mark-down-cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "mark↓ CLI for building snippet manifests",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"main": "dist/index.mjs",
|
|
10
10
|
"module": "dist/index.mjs",
|
|
11
11
|
"types": "dist/index.d.ts",
|
|
12
|
+
"files": ["dist"],
|
|
12
13
|
"publishConfig": {
|
|
13
14
|
"access": "public"
|
|
14
15
|
},
|
|
@@ -17,7 +18,7 @@
|
|
|
17
18
|
"dev": "tsup --config tsup.config.ts --watch"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
20
|
-
"@mzebley/mark-down": "^1.2.
|
|
21
|
+
"@mzebley/mark-down": "^1.2.2",
|
|
21
22
|
"cheerio": "^1.0.0",
|
|
22
23
|
"chokidar": "^3.6.0",
|
|
23
24
|
"commander": "^11.1.0",
|
package/src/compile-page.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { load as loadHtml } from "cheerio";
|
|
4
|
-
import { parseFrontMatter, renderMarkdown, type SnippetMeta } from "@mzebley/mark-down";
|
|
5
|
-
import { logEvent } from "./logger.js";
|
|
6
|
-
|
|
7
|
-
export interface CompilePageOptions {
|
|
8
|
-
manifest?: string;
|
|
9
|
-
outDir?: string;
|
|
10
|
-
inPlace?: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const DEFAULT_OUT_DIR = "dist";
|
|
14
|
-
|
|
15
|
-
export async function compilePage(inputHtml: string, options: CompilePageOptions = {}): Promise<string> {
|
|
16
|
-
const sourcePath = path.resolve(inputHtml);
|
|
17
|
-
await assertExists(sourcePath, `Input HTML file not found at '${inputHtml}'.`);
|
|
18
|
-
|
|
19
|
-
const manifestPath = await resolveManifestPath(sourcePath, options.manifest);
|
|
20
|
-
const manifestDir = path.dirname(manifestPath);
|
|
21
|
-
const manifest = await loadManifest(manifestPath);
|
|
22
|
-
|
|
23
|
-
const rawHtml = await fs.readFile(sourcePath, "utf8");
|
|
24
|
-
const doctypeMatch = rawHtml.match(/^(<!doctype[^>]*>\s*)/i);
|
|
25
|
-
const doctype = doctypeMatch?.[1] ?? "";
|
|
26
|
-
const dom = loadHtml(rawHtml, { decodeEntities: false });
|
|
27
|
-
|
|
28
|
-
const targets = dom("[data-snippet]").toArray();
|
|
29
|
-
for (const node of targets) {
|
|
30
|
-
const element = dom(node);
|
|
31
|
-
const slug = element.attr("data-snippet");
|
|
32
|
-
if (!slug) {
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
const entry = manifest.find((item) => item.slug === slug);
|
|
36
|
-
if (!entry) {
|
|
37
|
-
console.warn(`mark↓: no snippet found for "${slug}"`);
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const snippetPath = path.resolve(manifestDir, entry.path);
|
|
42
|
-
let raw: string;
|
|
43
|
-
try {
|
|
44
|
-
raw = await fs.readFile(snippetPath, "utf8");
|
|
45
|
-
} catch (error) {
|
|
46
|
-
console.warn(`mark↓: failed to read snippet at '${entry.path}'`, error);
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
let body = raw;
|
|
51
|
-
let frontMatterSlug: string | undefined;
|
|
52
|
-
try {
|
|
53
|
-
const frontMatter = parseFrontMatter(raw);
|
|
54
|
-
body = frontMatter.content;
|
|
55
|
-
frontMatterSlug = frontMatter.slug;
|
|
56
|
-
} catch (error) {
|
|
57
|
-
console.warn(`mark↓: failed to parse front matter for '${entry.path}'`, error);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const html = renderMarkdown(body);
|
|
61
|
-
element.html(html);
|
|
62
|
-
|
|
63
|
-
if (!element.attr("id")) {
|
|
64
|
-
element.attr("id", frontMatterSlug ?? `snippet-${slug}`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const outputDir = options.inPlace ? path.dirname(sourcePath) : path.resolve(options.outDir ?? DEFAULT_OUT_DIR);
|
|
69
|
-
if (!options.inPlace) {
|
|
70
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
71
|
-
}
|
|
72
|
-
const outputPath = options.inPlace
|
|
73
|
-
? sourcePath
|
|
74
|
-
: path.join(outputDir, path.basename(sourcePath));
|
|
75
|
-
|
|
76
|
-
const outputHtml = `${doctype}${dom.html() ?? ""}`;
|
|
77
|
-
await fs.writeFile(outputPath, outputHtml);
|
|
78
|
-
|
|
79
|
-
logEvent("info", "compile_page.written", { outputPath });
|
|
80
|
-
return outputPath;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async function resolveManifestPath(inputHtml: string, manifestFlag?: string): Promise<string> {
|
|
84
|
-
const manifestPath = manifestFlag
|
|
85
|
-
? path.resolve(manifestFlag)
|
|
86
|
-
: path.join(path.dirname(path.resolve(inputHtml)), "snippets-index.json");
|
|
87
|
-
await assertExists(manifestPath, `Manifest file not found at '${manifestPath}'.`);
|
|
88
|
-
return manifestPath;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async function loadManifest(manifestPath: string): Promise<SnippetMeta[]> {
|
|
92
|
-
let raw: string;
|
|
93
|
-
try {
|
|
94
|
-
raw = await fs.readFile(manifestPath, "utf8");
|
|
95
|
-
} catch (error) {
|
|
96
|
-
throw new Error(`Failed to read manifest at '${manifestPath}': ${String(error)}`);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
const parsed = JSON.parse(raw);
|
|
101
|
-
if (!Array.isArray(parsed)) {
|
|
102
|
-
throw new Error("Manifest must be a JSON array.");
|
|
103
|
-
}
|
|
104
|
-
return parsed as SnippetMeta[];
|
|
105
|
-
} catch (error) {
|
|
106
|
-
throw new Error(`Failed to parse manifest at '${manifestPath}': ${String(error)}`);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async function assertExists(target: string, message: string) {
|
|
111
|
-
try {
|
|
112
|
-
await fs.access(target);
|
|
113
|
-
} catch {
|
|
114
|
-
throw new Error(message);
|
|
115
|
-
}
|
|
116
|
-
}
|
package/src/errors.ts
DELETED
package/src/index.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from "commander";
|
|
3
|
-
import { buildManifestFile } from "./manifest.js";
|
|
4
|
-
import { watch as watchSnippets } from "./watch.js";
|
|
5
|
-
import { brand, logEvent } from "./logger.js";
|
|
6
|
-
import { DuplicateSlugError } from "./errors.js";
|
|
7
|
-
import { compilePage } from "./compile-page.js";
|
|
8
|
-
|
|
9
|
-
const program = new Command();
|
|
10
|
-
program
|
|
11
|
-
.name("mark-down")
|
|
12
|
-
.description(`${brand} CLI for building snippet manifests`)
|
|
13
|
-
.version("1.2.1");
|
|
14
|
-
|
|
15
|
-
program
|
|
16
|
-
.command("build")
|
|
17
|
-
.argument("[sourceDir]", "directory containing snippets", "content/snippets")
|
|
18
|
-
.option("-o, --output <path>", "where to write snippets-index.json")
|
|
19
|
-
.action(async (sourceDir: string, options: { output?: string }) => {
|
|
20
|
-
try {
|
|
21
|
-
const result = await buildManifestFile({ sourceDir, outputPath: options.output });
|
|
22
|
-
logEvent("info", "manifest.written", {
|
|
23
|
-
outputPath: result.outputPath,
|
|
24
|
-
snippetCount: result.manifest.length
|
|
25
|
-
});
|
|
26
|
-
} catch (error) {
|
|
27
|
-
handleError(error);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
program
|
|
32
|
-
.command("watch")
|
|
33
|
-
.argument("[sourceDir]", "directory containing snippets", "content/snippets")
|
|
34
|
-
.option("-o, --output <path>", "where to write snippets-index.json")
|
|
35
|
-
.action(async (sourceDir: string, options: { output?: string }) => {
|
|
36
|
-
try {
|
|
37
|
-
await watchSnippets(sourceDir, options.output);
|
|
38
|
-
} catch (error) {
|
|
39
|
-
handleError(error);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
program
|
|
44
|
-
.command("compile-page")
|
|
45
|
-
.argument("<inputHtml>", "HTML file containing data-snippet placeholders")
|
|
46
|
-
.option("--manifest <path>", "path to snippets-index.json")
|
|
47
|
-
.option("--outDir <path>", "output directory for compiled HTML", "dist")
|
|
48
|
-
.option("--inPlace", "overwrite the input HTML file instead of writing to outDir")
|
|
49
|
-
.action(async (inputHtml: string, options: { manifest?: string; outDir?: string; inPlace?: boolean }) => {
|
|
50
|
-
try {
|
|
51
|
-
await compilePage(inputHtml, {
|
|
52
|
-
manifest: options.manifest,
|
|
53
|
-
outDir: options.outDir,
|
|
54
|
-
inPlace: options.inPlace
|
|
55
|
-
});
|
|
56
|
-
} catch (error) {
|
|
57
|
-
handleError(error);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
program.parseAsync(process.argv).catch(handleError);
|
|
62
|
-
|
|
63
|
-
function handleError(error: unknown) {
|
|
64
|
-
const err = error as Error;
|
|
65
|
-
if (err instanceof DuplicateSlugError) {
|
|
66
|
-
logEvent("error", "manifest.duplicate_slug", {
|
|
67
|
-
message: err.message,
|
|
68
|
-
slugs: err.duplicates
|
|
69
|
-
});
|
|
70
|
-
process.exit(2);
|
|
71
|
-
}
|
|
72
|
-
logEvent("error", "cli.error", {
|
|
73
|
-
message: err.message,
|
|
74
|
-
stack: err.stack
|
|
75
|
-
});
|
|
76
|
-
process.exit(1);
|
|
77
|
-
}
|
package/src/logger.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export const brand = "mark↓";
|
|
2
|
-
|
|
3
|
-
export type LogLevel = "info" | "warn" | "error";
|
|
4
|
-
|
|
5
|
-
export interface LogFields {
|
|
6
|
-
message?: string;
|
|
7
|
-
[key: string]: unknown;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function logEvent(level: LogLevel, event: string, fields: LogFields = {}) {
|
|
11
|
-
const entry = {
|
|
12
|
-
brand,
|
|
13
|
-
level,
|
|
14
|
-
event,
|
|
15
|
-
timestamp: new Date().toISOString(),
|
|
16
|
-
...fields
|
|
17
|
-
};
|
|
18
|
-
const output = `${JSON.stringify(entry)}\n`;
|
|
19
|
-
const stream = level === "error" ? process.stderr : process.stdout;
|
|
20
|
-
stream.write(output);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function log(message: string, fields?: LogFields) {
|
|
24
|
-
if (fields) {
|
|
25
|
-
logEvent("info", message, fields);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
logEvent("info", "message", { message });
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function logError(message: string, fields?: LogFields) {
|
|
32
|
-
if (fields) {
|
|
33
|
-
logEvent("error", message, fields);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
logEvent("error", "message", { message });
|
|
37
|
-
}
|
package/src/manifest.ts
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import fg from "fast-glob";
|
|
4
|
-
import matter from "gray-matter";
|
|
5
|
-
import YAML from "yaml";
|
|
6
|
-
import { normalizeSlug, type SnippetMeta } from "@mzebley/mark-down";
|
|
7
|
-
import { DuplicateSlugError } from "./errors.js";
|
|
8
|
-
|
|
9
|
-
const MATTER_OPTIONS = {
|
|
10
|
-
engines: {
|
|
11
|
-
yaml: (source: string) => YAML.parse(source) ?? {}
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export interface BuildOptions {
|
|
16
|
-
sourceDir: string;
|
|
17
|
-
outputPath?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface BuildResult {
|
|
21
|
-
manifest: SnippetMeta[];
|
|
22
|
-
outputPath: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export async function buildManifestFile(options: BuildOptions): Promise<BuildResult> {
|
|
26
|
-
const manifest = await buildManifest(options.sourceDir);
|
|
27
|
-
const target = options.outputPath ?? path.join(options.sourceDir, "snippets-index.json");
|
|
28
|
-
await fs.writeFile(target, JSON.stringify(manifest, null, 2));
|
|
29
|
-
return { manifest, outputPath: target };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function buildManifest(sourceDir: string): Promise<SnippetMeta[]> {
|
|
33
|
-
const cwd = path.resolve(sourceDir);
|
|
34
|
-
const files = await fg(["**/*.md"], { cwd, absolute: true });
|
|
35
|
-
const manifest: SnippetMeta[] = [];
|
|
36
|
-
|
|
37
|
-
for (const absolutePath of files) {
|
|
38
|
-
const relativePath = path.relative(cwd, absolutePath);
|
|
39
|
-
const normalizedPath = toPosix(relativePath);
|
|
40
|
-
const content = await fs.readFile(absolutePath, "utf8");
|
|
41
|
-
const parsed = matter(content, MATTER_OPTIONS);
|
|
42
|
-
const snippet = createSnippet(normalizedPath, parsed.data ?? {});
|
|
43
|
-
if (snippet.draft) {
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
manifest.push(snippet);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
ensureUniqueSlugs(manifest);
|
|
50
|
-
|
|
51
|
-
manifest.sort((a, b) => {
|
|
52
|
-
const orderA = typeof a.order === "number" ? a.order : Number.POSITIVE_INFINITY;
|
|
53
|
-
const orderB = typeof b.order === "number" ? b.order : Number.POSITIVE_INFINITY;
|
|
54
|
-
if (orderA !== orderB) {
|
|
55
|
-
return orderA - orderB;
|
|
56
|
-
}
|
|
57
|
-
const titleA = a.title?.toLowerCase() ?? "";
|
|
58
|
-
const titleB = b.title?.toLowerCase() ?? "";
|
|
59
|
-
return titleA.localeCompare(titleB);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
return manifest;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function createSnippet(relativePath: string, frontMatter: Record<string, unknown>): SnippetMeta {
|
|
66
|
-
const group = deriveGroup(relativePath);
|
|
67
|
-
const slugSource = typeof frontMatter.slug === "string" && frontMatter.slug.trim().length
|
|
68
|
-
? frontMatter.slug
|
|
69
|
-
: relativePath.replace(/\.md$/i, "");
|
|
70
|
-
const slug = normalizeSlug(slugSource);
|
|
71
|
-
|
|
72
|
-
const { title, order, type, tags, draft } = normalizeKnownFields(frontMatter);
|
|
73
|
-
const extra = collectExtra(frontMatter);
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
slug,
|
|
77
|
-
title,
|
|
78
|
-
order,
|
|
79
|
-
type,
|
|
80
|
-
tags,
|
|
81
|
-
draft,
|
|
82
|
-
path: relativePath,
|
|
83
|
-
group,
|
|
84
|
-
extra
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function normalizeKnownFields(data: Record<string, unknown>) {
|
|
89
|
-
return {
|
|
90
|
-
title: typeof data.title === "string" ? data.title : undefined,
|
|
91
|
-
order: typeof data.order === "number"
|
|
92
|
-
? data.order
|
|
93
|
-
: data.order === null
|
|
94
|
-
? null
|
|
95
|
-
: undefined,
|
|
96
|
-
type: typeof data.type === "string" ? data.type : undefined,
|
|
97
|
-
tags: normalizeTags(data.tags),
|
|
98
|
-
draft: data.draft === true ? true : undefined
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function collectExtra(data: Record<string, unknown>): Record<string, unknown> | undefined {
|
|
103
|
-
const extra: Record<string, unknown> = {};
|
|
104
|
-
const reserved = new Set(["slug", "title", "order", "type", "tags", "draft"]);
|
|
105
|
-
for (const [key, value] of Object.entries(data)) {
|
|
106
|
-
if (reserved.has(key)) {
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
extra[key] = value;
|
|
110
|
-
}
|
|
111
|
-
return Object.keys(extra).length ? extra : undefined;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function normalizeTags(value: unknown): string[] | undefined {
|
|
115
|
-
if (!value) {
|
|
116
|
-
return undefined;
|
|
117
|
-
}
|
|
118
|
-
if (Array.isArray(value)) {
|
|
119
|
-
return value.map((entry) => String(entry));
|
|
120
|
-
}
|
|
121
|
-
if (typeof value === "string") {
|
|
122
|
-
return value
|
|
123
|
-
.split(",")
|
|
124
|
-
.map((entry) => entry.trim())
|
|
125
|
-
.filter(Boolean);
|
|
126
|
-
}
|
|
127
|
-
return undefined;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function deriveGroup(relativePath: string): string {
|
|
131
|
-
const dirname = toPosix(path.dirname(relativePath));
|
|
132
|
-
if (dirname === "." || !dirname.length) {
|
|
133
|
-
return "root";
|
|
134
|
-
}
|
|
135
|
-
return dirname;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function toPosix(value: string): string {
|
|
139
|
-
return value.split(path.sep).join("/");
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function ensureUniqueSlugs(manifest: SnippetMeta[]) {
|
|
143
|
-
const seen = new Map<string, string>();
|
|
144
|
-
const duplicates = new Set<string>();
|
|
145
|
-
for (const snippet of manifest) {
|
|
146
|
-
if (seen.has(snippet.slug)) {
|
|
147
|
-
duplicates.add(snippet.slug);
|
|
148
|
-
} else {
|
|
149
|
-
seen.set(snippet.slug, snippet.path);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
if (duplicates.size) {
|
|
153
|
-
throw new DuplicateSlugError([...duplicates.values()]);
|
|
154
|
-
}
|
|
155
|
-
}
|
package/src/watch.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import chokidar from "chokidar";
|
|
3
|
-
import { buildManifestFile, type BuildResult } from "./manifest.js";
|
|
4
|
-
import { logEvent } from "./logger.js";
|
|
5
|
-
|
|
6
|
-
export async function watch(sourceDir: string, outputPath?: string) {
|
|
7
|
-
const cwd = path.resolve(sourceDir);
|
|
8
|
-
logEvent("info", "watch.start", {
|
|
9
|
-
directory: cwd,
|
|
10
|
-
outputPath: outputPath ?? path.join(cwd, "snippets-index.json")
|
|
11
|
-
});
|
|
12
|
-
await rebuild(cwd, outputPath);
|
|
13
|
-
|
|
14
|
-
const watcher = chokidar.watch(["**/*.md"], {
|
|
15
|
-
cwd,
|
|
16
|
-
ignoreInitial: true,
|
|
17
|
-
awaitWriteFinish: {
|
|
18
|
-
stabilityThreshold: 200,
|
|
19
|
-
pollInterval: 50
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const schedule = debounce(async () => {
|
|
24
|
-
await rebuild(cwd, outputPath);
|
|
25
|
-
}, 150);
|
|
26
|
-
|
|
27
|
-
watcher.on("all", (event, filePath) => {
|
|
28
|
-
logEvent("info", "watch.change", { event, file: filePath });
|
|
29
|
-
schedule();
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function rebuild(sourceDir: string, outputPath?: string): Promise<BuildResult | void> {
|
|
34
|
-
try {
|
|
35
|
-
const result = await buildManifestFile({ sourceDir, outputPath });
|
|
36
|
-
logEvent("info", "manifest.updated", {
|
|
37
|
-
outputPath: result.outputPath,
|
|
38
|
-
snippetCount: result.manifest.length
|
|
39
|
-
});
|
|
40
|
-
return result;
|
|
41
|
-
} catch (error) {
|
|
42
|
-
const err = error as Error;
|
|
43
|
-
logEvent("error", "manifest.update_failed", {
|
|
44
|
-
message: err.message,
|
|
45
|
-
stack: err.stack
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function debounce<T extends (...args: unknown[]) => Promise<unknown> | void>(
|
|
51
|
-
fn: T,
|
|
52
|
-
delay: number
|
|
53
|
-
) {
|
|
54
|
-
let timer: NodeJS.Timeout | null = null;
|
|
55
|
-
return (...args: Parameters<T>) => {
|
|
56
|
-
if (timer) {
|
|
57
|
-
clearTimeout(timer);
|
|
58
|
-
}
|
|
59
|
-
timer = setTimeout(() => {
|
|
60
|
-
timer = null;
|
|
61
|
-
void fn(...args);
|
|
62
|
-
}, delay);
|
|
63
|
-
};
|
|
64
|
-
}
|
package/tsconfig.json
DELETED
package/tsup.config.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from "tsup";
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
entry: ["src/index.ts"],
|
|
5
|
-
format: ["esm", "cjs"],
|
|
6
|
-
dts: true,
|
|
7
|
-
sourcemap: true,
|
|
8
|
-
splitting: false,
|
|
9
|
-
clean: true,
|
|
10
|
-
outExtension({ format }) {
|
|
11
|
-
return {
|
|
12
|
-
js: format === "esm" ? ".mjs" : ".cjs"
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
});
|