md-redline 0.1.0
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/LICENSE +21 -0
- package/README.md +169 -0
- package/bin/md-redline +255 -0
- package/bin/test-windows.ps1 +70 -0
- package/dist/assets/_baseFor-Ck08IaSF.js +1 -0
- package/dist/assets/arc-DI2g9LXK.js +1 -0
- package/dist/assets/architecture-YZFGNWBL-BDgMfc-b.js +1 -0
- package/dist/assets/architectureDiagram-Q4EWVU46-Dg1hcUEa.js +36 -0
- package/dist/assets/array-DOVTz2Mq.js +1 -0
- package/dist/assets/blockDiagram-DXYQGD6D-BAXkTCAk.js +132 -0
- package/dist/assets/c4Diagram-AHTNJAMY-BIkgwQSx.js +10 -0
- package/dist/assets/channel-DPCihw7y.js +1 -0
- package/dist/assets/chunk-2KRD3SAO-Dc_tBGsw.js +1 -0
- package/dist/assets/chunk-336JU56O-Dhi-ID9Y.js +2 -0
- package/dist/assets/chunk-426QAEUC-DnFdrNMW.js +1 -0
- package/dist/assets/chunk-4BX2VUAB-Z63FkGov.js +1 -0
- package/dist/assets/chunk-4TB4RGXK-BAiBlfyy.js +206 -0
- package/dist/assets/chunk-55IACEB6-BXDWXbxy.js +1 -0
- package/dist/assets/chunk-5FUZZQ4R-C72e1c_O.js +62 -0
- package/dist/assets/chunk-5PVQY5BW-BBHW_uCu.js +2 -0
- package/dist/assets/chunk-67CJDMHE-3Cf_D9m6.js +1 -0
- package/dist/assets/chunk-7N4EOEYR-DAXUXJ2c.js +1 -0
- package/dist/assets/chunk-AA7GKIK3-Dr7fOryc.js +1 -0
- package/dist/assets/chunk-BSJP7CBP-BmsSs1Nt.js +1 -0
- package/dist/assets/chunk-CIAEETIT-QDzV-X_Y.js +1 -0
- package/dist/assets/chunk-EDXVE4YY-C25WFHxY.js +1 -0
- package/dist/assets/chunk-ENJZ2VHE-_OzxcZOU.js +10 -0
- package/dist/assets/chunk-FMBD7UC4-CjsTKY4u.js +15 -0
- package/dist/assets/chunk-FOC6F5B3-g-xaH5nc.js +1 -0
- package/dist/assets/chunk-ICPOFSXX-iKiUSjDK.js +121 -0
- package/dist/assets/chunk-K5T4RW27-CKR-lPBN.js +94 -0
- package/dist/assets/chunk-KGLVRYIC-DRccT-B_.js +1 -0
- package/dist/assets/chunk-LIHQZDEY-DTbMwMXj.js +1 -0
- package/dist/assets/chunk-ORNJ4GCN-DlerdcWX.js +1 -0
- package/dist/assets/chunk-OYMX7WX6-Dekv1on2.js +231 -0
- package/dist/assets/chunk-QZHKN3VN-BHu0RdKl.js +1 -0
- package/dist/assets/chunk-U2HBQHQK-BvtlVHAg.js +70 -0
- package/dist/assets/chunk-X2U36JSP-BI_g8mub.js +1 -0
- package/dist/assets/chunk-XPW4576I-B39JkmSE.js +32 -0
- package/dist/assets/chunk-YZCP3GAM-BfPcXRm2.js +1 -0
- package/dist/assets/chunk-ZZ45TVLE-Bg4q68wZ.js +1 -0
- package/dist/assets/classDiagram-6PBFFD2Q-p73p727_.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-C4Ftpivp.js +1 -0
- package/dist/assets/clone-CI9aUwHe.js +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-7BpAeDh5.js +1 -0
- package/dist/assets/cytoscape.esm-DoTFyJaN.js +321 -0
- package/dist/assets/dagre-CilMRazv.js +1 -0
- package/dist/assets/dagre-KV5264BT-DDMqpjkB.js +4 -0
- package/dist/assets/defaultLocale-Ck2Xxk-C.js +1 -0
- package/dist/assets/diagram-5BDNPKRD-BFeyfnCx.js +10 -0
- package/dist/assets/diagram-G4DWMVQ6-DoqT-PtF.js +24 -0
- package/dist/assets/diagram-MMDJMWI5-BPV6KADk.js +43 -0
- package/dist/assets/diagram-TYMM5635-okvcTBtl.js +24 -0
- package/dist/assets/dist-C_eddq6m.js +1 -0
- package/dist/assets/erDiagram-SMLLAGMA-Dl-Ixy8n.js +85 -0
- package/dist/assets/flatten-B8XIuT0x.js +1 -0
- package/dist/assets/flowDiagram-DWJPFMVM-CsqWAx5r.js +162 -0
- package/dist/assets/ganttDiagram-T4ZO3ILL-mIt6zVeF.js +292 -0
- package/dist/assets/gitGraph-7Q5UKJZL-COXHGMvj.js +1 -0
- package/dist/assets/gitGraphDiagram-UUTBAWPF-syVqZJX_.js +106 -0
- package/dist/assets/graphlib-Bpd0q3yO.js +1 -0
- package/dist/assets/index-BoggyWS0.css +2 -0
- package/dist/assets/index-aLvjHQW4.js +104 -0
- package/dist/assets/info-OMHHGYJF-B-0wfxwL.js +1 -0
- package/dist/assets/infoDiagram-42DDH7IO-C0_uqsVa.js +2 -0
- package/dist/assets/init-Bft5Ffpj.js +1 -0
- package/dist/assets/isEmpty-BrFi5AqV.js +1 -0
- package/dist/assets/ishikawaDiagram-UXIWVN3A-CTjFbDBV.js +70 -0
- package/dist/assets/journeyDiagram-VCZTEJTY-BDBcej1q.js +139 -0
- package/dist/assets/kanban-definition-6JOO6SKY-Ylgzakw7.js +89 -0
- package/dist/assets/katex-Uj9wLT16.js +265 -0
- package/dist/assets/line-CRxEwpOv.js +1 -0
- package/dist/assets/linear-PDPfFByd.js +1 -0
- package/dist/assets/mermaid-parser.core-CY-XNOOy.js +4 -0
- package/dist/assets/mermaid.core-BPlTADIX.js +11 -0
- package/dist/assets/mindmap-definition-QFDTVHPH-TefzJnBM.js +96 -0
- package/dist/assets/ordinal-DIg8h6NI.js +1 -0
- package/dist/assets/packet-4T2RLAQJ-BW1T_A-C.js +1 -0
- package/dist/assets/path-DfRbCp9y.js +1 -0
- package/dist/assets/pie-ZZUOXDRM-DkKU-SFu.js +1 -0
- package/dist/assets/pieDiagram-DEJITSTG-BCXuaeEy.js +30 -0
- package/dist/assets/quadrantDiagram-34T5L4WZ-VSBAicWL.js +7 -0
- package/dist/assets/radar-PYXPWWZC-CYvTacKJ.js +1 -0
- package/dist/assets/reduce-CV2X8n1a.js +1 -0
- package/dist/assets/requirementDiagram-MS252O5E-4NeL9Z6J.js +84 -0
- package/dist/assets/rough.esm-Bbn_-PMU.js +1 -0
- package/dist/assets/sankeyDiagram-XADWPNL6-DMBSDnrH.js +10 -0
- package/dist/assets/sequenceDiagram-FGHM5R23-DVpzcZUi.js +157 -0
- package/dist/assets/src-PKe5NtkK.js +1 -0
- package/dist/assets/stateDiagram-FHFEXIEX-BkHTlCjL.js +1 -0
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-nMeWu9fP.js +1 -0
- package/dist/assets/timeline-definition-GMOUNBTQ-CyLt92nf.js +120 -0
- package/dist/assets/treeView-SZITEDCU-BUgcJ4eR.js +1 -0
- package/dist/assets/treemap-W4RFUUIX-BIWGQ4Pw.js +1 -0
- package/dist/assets/vennDiagram-DHZGUBPP-BCK0xB_m.js +34 -0
- package/dist/assets/wardley-RL74JXVD-DMZZRlby.js +1 -0
- package/dist/assets/wardleyDiagram-NUSXRM2D-BisBgfsF.js +20 -0
- package/dist/assets/xychartDiagram-5P7HB3ND-D_REDciv.js +7 -0
- package/dist/favicon.svg +15 -0
- package/dist/index.html +14 -0
- package/dist/screenshot.png +0 -0
- package/index.html +13 -0
- package/package.json +105 -0
- package/public/favicon.svg +15 -0
- package/public/screenshot.png +0 -0
- package/server/index.test.ts +814 -0
- package/server/index.ts +736 -0
- package/server/preferences.test.ts +126 -0
- package/server/preferences.ts +76 -0
- package/src/App.tsx +1620 -0
- package/src/components/ActionButton.tsx +41 -0
- package/src/components/CommandPalette.tsx +191 -0
- package/src/components/CommentCard.tsx +556 -0
- package/src/components/CommentForm.tsx +285 -0
- package/src/components/CommentSidebar.tsx +428 -0
- package/src/components/ConfirmDialog.tsx +64 -0
- package/src/components/ContextMenu.tsx +220 -0
- package/src/components/DragHandles.tsx +48 -0
- package/src/components/FileExplorer.tsx +251 -0
- package/src/components/FileOpener.tsx +304 -0
- package/src/components/IconButton.tsx +32 -0
- package/src/components/KeyboardShortcutsPanel.tsx +136 -0
- package/src/components/MarkdownViewer.tsx +682 -0
- package/src/components/RawView.tsx +798 -0
- package/src/components/SearchBar.tsx +129 -0
- package/src/components/Separator.tsx +7 -0
- package/src/components/SettingsPanel.tsx +813 -0
- package/src/components/SplitIconButton.tsx +133 -0
- package/src/components/TabBar.tsx +594 -0
- package/src/components/TableOfContents.tsx +70 -0
- package/src/components/ThemeSelector.tsx +159 -0
- package/src/components/Toast.tsx +99 -0
- package/src/components/Toolbar.tsx +161 -0
- package/src/components/iconButtonVariants.ts +19 -0
- package/src/components/rawView.test.ts +291 -0
- package/src/contexts/SettingsContext.tsx +120 -0
- package/src/hooks/useAuthor.test.ts +58 -0
- package/src/hooks/useAuthor.ts +69 -0
- package/src/hooks/useAutoResize.ts +20 -0
- package/src/hooks/useCommentCardTriggers.ts +20 -0
- package/src/hooks/useComments.test.ts +773 -0
- package/src/hooks/useComments.ts +332 -0
- package/src/hooks/useContextMenu.ts +48 -0
- package/src/hooks/useContextMenuItems.ts +392 -0
- package/src/hooks/useDiffSnapshot.test.ts +130 -0
- package/src/hooks/useDiffSnapshot.ts +67 -0
- package/src/hooks/useDragHandles.ts +417 -0
- package/src/hooks/useFileWatcher.ts +45 -0
- package/src/hooks/useHeadingTracking.ts +84 -0
- package/src/hooks/useMermaidRenderer.ts +75 -0
- package/src/hooks/useModalState.ts +22 -0
- package/src/hooks/usePageVisible.test.ts +69 -0
- package/src/hooks/usePageVisible.ts +19 -0
- package/src/hooks/usePaneLayout.test.ts +108 -0
- package/src/hooks/usePaneLayout.ts +102 -0
- package/src/hooks/useRecentFiles.test.ts +103 -0
- package/src/hooks/useRecentFiles.ts +99 -0
- package/src/hooks/useResizablePanel.test.ts +84 -0
- package/src/hooks/useResizablePanel.ts +118 -0
- package/src/hooks/useSearch.test.ts +72 -0
- package/src/hooks/useSearch.ts +53 -0
- package/src/hooks/useSelection.ts +48 -0
- package/src/hooks/useSessionPersistence.test.ts +59 -0
- package/src/hooks/useSessionPersistence.ts +43 -0
- package/src/hooks/useTabs.test.ts +127 -0
- package/src/hooks/useTabs.ts +561 -0
- package/src/hooks/useThemePersistence.ts +41 -0
- package/src/hooks/useToast.ts +27 -0
- package/src/index.css +1047 -0
- package/src/lib/agent-prompts.test.ts +34 -0
- package/src/lib/agent-prompts.ts +68 -0
- package/src/lib/comment-editor-state.ts +6 -0
- package/src/lib/comment-parser.test.ts +1959 -0
- package/src/lib/comment-parser.ts +1021 -0
- package/src/lib/diff.test.ts +164 -0
- package/src/lib/diff.ts +139 -0
- package/src/lib/heading-slugs.test.ts +85 -0
- package/src/lib/heading-slugs.ts +44 -0
- package/src/lib/http.test.ts +43 -0
- package/src/lib/http.ts +29 -0
- package/src/lib/mermaid-highlights.test.ts +517 -0
- package/src/lib/mermaid-highlights.ts +936 -0
- package/src/lib/mermaid-renderer.test.ts +114 -0
- package/src/lib/mermaid-renderer.ts +89 -0
- package/src/lib/path-utils.test.ts +17 -0
- package/src/lib/path-utils.ts +7 -0
- package/src/lib/platform.test.ts +58 -0
- package/src/lib/platform.ts +14 -0
- package/src/lib/preferences-client.test.ts +177 -0
- package/src/lib/preferences-client.ts +94 -0
- package/src/lib/selection-resolver.test.ts +118 -0
- package/src/lib/selection-resolver.ts +37 -0
- package/src/lib/settings.test.ts +152 -0
- package/src/lib/settings.ts +78 -0
- package/src/lib/shortcut-label.tsx +18 -0
- package/src/lib/themes.ts +21 -0
- package/src/lib/visible-text.test.ts +86 -0
- package/src/lib/visible-text.ts +77 -0
- package/src/main.tsx +22 -0
- package/src/markdown/pipeline.test.ts +82 -0
- package/src/markdown/pipeline.ts +33 -0
- package/src/types.test.ts +43 -0
- package/src/types.ts +46 -0
- package/tsconfig.app.json +28 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +26 -0
- package/vite.config.ts +50 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { mkdtemp, rm, writeFile, readFile } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { tmpdir } from 'os';
|
|
5
|
+
import { readPreferences, writePreferences } from './preferences';
|
|
6
|
+
|
|
7
|
+
let testDir: string;
|
|
8
|
+
|
|
9
|
+
beforeAll(async () => {
|
|
10
|
+
testDir = await mkdtemp(join(tmpdir(), 'md-redline-prefs-'));
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterAll(async () => {
|
|
14
|
+
await rm(testDir, { recursive: true });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
// Clean up the prefs file between tests
|
|
19
|
+
try {
|
|
20
|
+
await rm(join(testDir, '.md-redline.json'));
|
|
21
|
+
} catch {
|
|
22
|
+
/* doesn't exist */
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
await rm(join(testDir, '.md-redline.json.tmp'));
|
|
26
|
+
} catch {
|
|
27
|
+
/* doesn't exist */
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('readPreferences', () => {
|
|
32
|
+
it('returns {} when dotfile does not exist', async () => {
|
|
33
|
+
const prefs = await readPreferences(testDir);
|
|
34
|
+
expect(prefs).toEqual({});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('returns parsed content when dotfile exists', async () => {
|
|
38
|
+
await writeFile(
|
|
39
|
+
join(testDir, '.md-redline.json'),
|
|
40
|
+
JSON.stringify({ author: 'Alice', theme: 'dark' }),
|
|
41
|
+
);
|
|
42
|
+
const prefs = await readPreferences(testDir);
|
|
43
|
+
expect(prefs).toEqual({ author: 'Alice', theme: 'dark' });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('returns {} on invalid JSON', async () => {
|
|
47
|
+
await writeFile(join(testDir, '.md-redline.json'), 'not json at all {{{');
|
|
48
|
+
const prefs = await readPreferences(testDir);
|
|
49
|
+
expect(prefs).toEqual({});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('returns {} if file contains a JSON array', async () => {
|
|
53
|
+
await writeFile(join(testDir, '.md-redline.json'), '["not", "an", "object"]');
|
|
54
|
+
const prefs = await readPreferences(testDir);
|
|
55
|
+
expect(prefs).toEqual({});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('returns {} if file contains a JSON null', async () => {
|
|
59
|
+
await writeFile(join(testDir, '.md-redline.json'), 'null');
|
|
60
|
+
const prefs = await readPreferences(testDir);
|
|
61
|
+
expect(prefs).toEqual({});
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('writePreferences', () => {
|
|
66
|
+
it('creates file when it does not exist', async () => {
|
|
67
|
+
const result = await writePreferences(testDir, { author: 'Bob' });
|
|
68
|
+
expect(result).toEqual({ author: 'Bob' });
|
|
69
|
+
|
|
70
|
+
const raw = await readFile(join(testDir, '.md-redline.json'), 'utf-8');
|
|
71
|
+
const parsed = JSON.parse(raw);
|
|
72
|
+
expect(parsed.author).toBe('Bob');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('merges with existing content', async () => {
|
|
76
|
+
await writePreferences(testDir, { author: 'Alice', theme: 'light' });
|
|
77
|
+
const result = await writePreferences(testDir, { theme: 'dark' });
|
|
78
|
+
expect(result).toEqual({ author: 'Alice', theme: 'dark' });
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('preserves keys not in the patch', async () => {
|
|
82
|
+
await writePreferences(testDir, {
|
|
83
|
+
author: 'Alice',
|
|
84
|
+
theme: 'dark',
|
|
85
|
+
recentFiles: [{ path: '/a.md', name: 'a.md', openedAt: '2026-01-01T00:00:00Z' }],
|
|
86
|
+
});
|
|
87
|
+
const result = await writePreferences(testDir, { author: 'Bob' });
|
|
88
|
+
expect(result.author).toBe('Bob');
|
|
89
|
+
expect(result.theme).toBe('dark');
|
|
90
|
+
expect(result.recentFiles).toHaveLength(1);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('replaces top-level keys entirely (no deep merge)', async () => {
|
|
94
|
+
await writePreferences(testDir, {
|
|
95
|
+
settings: { commentMaxLength: 500, enableResolve: true },
|
|
96
|
+
});
|
|
97
|
+
const result = await writePreferences(testDir, {
|
|
98
|
+
settings: { commentMaxLength: 1000 },
|
|
99
|
+
});
|
|
100
|
+
// enableResolve should be gone — settings was replaced entirely
|
|
101
|
+
expect(result.settings).toEqual({ commentMaxLength: 1000 });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('serializes concurrent writes correctly', async () => {
|
|
105
|
+
// Fire multiple writes concurrently
|
|
106
|
+
const promises = [
|
|
107
|
+
writePreferences(testDir, { author: 'A' }),
|
|
108
|
+
writePreferences(testDir, { theme: 'dark' }),
|
|
109
|
+
writePreferences(testDir, { author: 'B' }),
|
|
110
|
+
];
|
|
111
|
+
await Promise.all(promises);
|
|
112
|
+
|
|
113
|
+
const final = await readPreferences(testDir);
|
|
114
|
+
// Both author and theme should be present (all writes completed)
|
|
115
|
+
expect(final.theme).toBe('dark');
|
|
116
|
+
// Author should be either A or B (last write wins), but must exist
|
|
117
|
+
expect(typeof final.author).toBe('string');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('writes valid JSON with trailing newline', async () => {
|
|
121
|
+
await writePreferences(testDir, { author: 'Test' });
|
|
122
|
+
const raw = await readFile(join(testDir, '.md-redline.json'), 'utf-8');
|
|
123
|
+
expect(raw.endsWith('\n')).toBe(true);
|
|
124
|
+
expect(() => JSON.parse(raw)).not.toThrow();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { readFile, rename, open } from 'fs/promises';
|
|
2
|
+
import { unlinkSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
const PREFS_FILENAME = '.md-redline.json';
|
|
6
|
+
|
|
7
|
+
export interface RecentFile {
|
|
8
|
+
path: string;
|
|
9
|
+
name: string;
|
|
10
|
+
openedAt: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AppSettings {
|
|
14
|
+
templates?: { label: string; text: string }[];
|
|
15
|
+
commentMaxLength?: number;
|
|
16
|
+
showTemplatesByDefault?: boolean;
|
|
17
|
+
enableResolve?: boolean;
|
|
18
|
+
quickComment?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface Preferences {
|
|
22
|
+
author?: string;
|
|
23
|
+
settings?: AppSettings;
|
|
24
|
+
theme?: string;
|
|
25
|
+
recentFiles?: RecentFile[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function prefsPath(homeDir: string): string {
|
|
29
|
+
return join(homeDir, PREFS_FILENAME);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function readPreferences(homeDir: string): Promise<Preferences> {
|
|
33
|
+
try {
|
|
34
|
+
const raw = await readFile(prefsPath(homeDir), 'utf-8');
|
|
35
|
+
const parsed = JSON.parse(raw);
|
|
36
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {};
|
|
37
|
+
return parsed as Preferences;
|
|
38
|
+
} catch {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Write lock to serialize concurrent writes
|
|
44
|
+
let writeLock: Promise<void> = Promise.resolve();
|
|
45
|
+
|
|
46
|
+
export async function writePreferences(
|
|
47
|
+
homeDir: string,
|
|
48
|
+
patch: Partial<Preferences>,
|
|
49
|
+
): Promise<Preferences> {
|
|
50
|
+
const result = await new Promise<Preferences>((resolve, reject) => {
|
|
51
|
+
writeLock = writeLock.then(async () => {
|
|
52
|
+
try {
|
|
53
|
+
const existing = await readPreferences(homeDir);
|
|
54
|
+
const merged = { ...existing, ...patch };
|
|
55
|
+
const filePath = prefsPath(homeDir);
|
|
56
|
+
const tmpPath = filePath + '.tmp';
|
|
57
|
+
try {
|
|
58
|
+
unlinkSync(tmpPath);
|
|
59
|
+
} catch {
|
|
60
|
+
// No existing file — fine
|
|
61
|
+
}
|
|
62
|
+
const fd = await open(tmpPath, 'wx');
|
|
63
|
+
try {
|
|
64
|
+
await fd.writeFile(JSON.stringify(merged, null, 2) + '\n', 'utf-8');
|
|
65
|
+
} finally {
|
|
66
|
+
await fd.close();
|
|
67
|
+
}
|
|
68
|
+
await rename(tmpPath, filePath);
|
|
69
|
+
resolve(merged);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
reject(err);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
return result;
|
|
76
|
+
}
|