@dhruv2mars/mdv 0.0.2 → 0.0.5

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,28 @@
1
+ export function assetNameFor(platform = process.platform, arch = process.arch) {
2
+ const ext = platform === 'win32' ? '.exe' : '';
3
+ return `mdv-${platform}-${arch}${ext}`;
4
+ }
5
+
6
+ export function findAssetUrl(release, asset) {
7
+ if (!release || !Array.isArray(release.assets)) return null;
8
+ const match = release.assets.find((item) => item?.name === asset);
9
+ if (!match) return null;
10
+ return match.browser_download_url || null;
11
+ }
12
+
13
+ export async function resolveReleaseAssetUrl({ version, asset, getRelease }) {
14
+ const tag = `tags/v${version}`;
15
+ try {
16
+ const tagged = await getRelease(tag);
17
+ const taggedUrl = findAssetUrl(tagged, asset);
18
+ if (taggedUrl) return taggedUrl;
19
+ } catch {}
20
+
21
+ try {
22
+ const latest = await getRelease('latest');
23
+ const latestUrl = findAssetUrl(latest, asset);
24
+ if (latestUrl) return latestUrl;
25
+ } catch {}
26
+
27
+ return null;
28
+ }
package/bin/install.js ADDED
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ chmodSync,
4
+ copyFileSync,
5
+ createWriteStream,
6
+ existsSync,
7
+ mkdirSync,
8
+ readFileSync,
9
+ rmSync,
10
+ renameSync
11
+ } from 'node:fs';
12
+ import { homedir } from 'node:os';
13
+ import { join } from 'node:path';
14
+ import https from 'node:https';
15
+ import { spawnSync } from 'node:child_process';
16
+ import { fileURLToPath } from 'node:url';
17
+ import { assetNameFor, resolveReleaseAssetUrl } from './install-lib.js';
18
+
19
+ const REPO = 'Dhruv2mars/mdv';
20
+
21
+ const installRoot = process.env.MDV_INSTALL_ROOT || join(homedir(), '.mdv');
22
+ const binDir = join(installRoot, 'bin');
23
+ const binName = process.platform === 'win32' ? 'mdv.exe' : 'mdv';
24
+ const dest = join(binDir, binName);
25
+
26
+ if (process.env.MDV_SKIP_DOWNLOAD === '1') process.exit(0);
27
+ if (existsSync(dest)) process.exit(0);
28
+
29
+ mkdirSync(binDir, { recursive: true });
30
+
31
+ const version = pkgVersion();
32
+ const asset = assetNameFor();
33
+ const url = `https://github.com/${REPO}/releases/download/v${version}/${asset}`;
34
+ const tmp = `${dest}.tmp-${Date.now()}`;
35
+
36
+ try {
37
+ console.error(`mdv: download ${asset} v${version}`);
38
+ await downloadWithFallback(url, version, asset, tmp);
39
+ if (process.platform !== 'win32') chmodSync(tmp, 0o755);
40
+ renameSync(tmp, dest);
41
+ process.exit(0);
42
+ } catch (err) {
43
+ try { rmSync(tmp, { force: true }); } catch {}
44
+
45
+ console.error(`mdv: download failed (${String(err)})`);
46
+
47
+ if (process.env.MDV_ALLOW_CARGO_FALLBACK === '1') {
48
+ if (cargoInstallFallback()) {
49
+ process.exit(0);
50
+ }
51
+ }
52
+
53
+ console.error('mdv: install incomplete. re-run with MDV_ALLOW_CARGO_FALLBACK=1 or wait for GitHub release assets.');
54
+ process.exit(0);
55
+ }
56
+
57
+ function pkgVersion() {
58
+ try {
59
+ const here = fileURLToPath(new URL('.', import.meta.url));
60
+ const p = readFileSync(join(here, '..', 'package.json'), 'utf8');
61
+ return JSON.parse(p).version;
62
+ } catch {
63
+ return process.env.npm_package_version || '0.0.0';
64
+ }
65
+ }
66
+
67
+ function download(url, outPath) {
68
+ return new Promise((resolve, reject) => {
69
+ const req = https.get(
70
+ url,
71
+ { headers: { 'User-Agent': 'mdv-installer', Accept: 'application/octet-stream' } },
72
+ (res) => {
73
+ if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
74
+ res.resume();
75
+ download(res.headers.location, outPath).then(resolve, reject);
76
+ return;
77
+ }
78
+
79
+ if (res.statusCode !== 200) {
80
+ res.resume();
81
+ reject(new Error(`http ${res.statusCode}`));
82
+ return;
83
+ }
84
+
85
+ const file = createWriteStream(outPath);
86
+ res.pipe(file);
87
+ file.on('finish', () => file.close(resolve));
88
+ file.on('error', reject);
89
+ }
90
+ );
91
+
92
+ req.on('error', reject);
93
+ });
94
+ }
95
+
96
+ async function downloadWithFallback(primaryUrl, version, asset, outPath) {
97
+ try {
98
+ await download(primaryUrl, outPath);
99
+ return;
100
+ } catch (primaryErr) {
101
+ const fallbackUrl = await resolveReleaseAssetUrl({
102
+ version,
103
+ asset,
104
+ getRelease: getRelease
105
+ });
106
+
107
+ if (!fallbackUrl || fallbackUrl === primaryUrl) {
108
+ throw primaryErr;
109
+ }
110
+
111
+ console.error(`mdv: fallback download ${fallbackUrl}`);
112
+ await download(fallbackUrl, outPath);
113
+ }
114
+ }
115
+
116
+ async function getRelease(kind) {
117
+ const base = `https://api.github.com/repos/${REPO}/releases`;
118
+ return requestJson(`${base}/${kind}`);
119
+ }
120
+
121
+ function requestJson(url) {
122
+ return new Promise((resolve, reject) => {
123
+ const headers = {
124
+ 'User-Agent': 'mdv-installer',
125
+ Accept: 'application/vnd.github+json'
126
+ };
127
+
128
+ if (process.env.GITHUB_TOKEN) {
129
+ headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
130
+ }
131
+
132
+ const req = https.get(url, { headers }, (res) => {
133
+ let data = '';
134
+ res.setEncoding('utf8');
135
+ res.on('data', (chunk) => {
136
+ data += chunk;
137
+ });
138
+ res.on('end', () => {
139
+ if ((res.statusCode || 500) >= 400) {
140
+ const err = new Error(`http ${res.statusCode}`);
141
+ err.status = res.statusCode;
142
+ reject(err);
143
+ return;
144
+ }
145
+ try {
146
+ resolve(JSON.parse(data || '{}'));
147
+ } catch (err) {
148
+ reject(err);
149
+ }
150
+ });
151
+ });
152
+
153
+ req.on('error', reject);
154
+ });
155
+ }
156
+
157
+ function cargoInstallFallback() {
158
+ const probe = spawnSync('cargo', ['--version'], { stdio: 'ignore' });
159
+ if (probe.status !== 0) return false;
160
+
161
+ console.error('mdv: cargo fallback install...');
162
+
163
+ const root = installRoot;
164
+ const install = spawnSync(
165
+ 'cargo',
166
+ [
167
+ 'install',
168
+ 'mdv-cli',
169
+ '--git',
170
+ `https://github.com/${REPO}.git`,
171
+ '--locked',
172
+ '--root',
173
+ root,
174
+ '--config',
175
+ 'net.git-fetch-with-cli=true'
176
+ ],
177
+ { stdio: 'inherit', env: { ...process.env, CARGO_NET_GIT_FETCH_WITH_CLI: 'true' } }
178
+ );
179
+
180
+ if (install.status !== 0) return false;
181
+
182
+ const built = join(root, 'bin', process.platform === 'win32' ? 'mdv-cli.exe' : 'mdv-cli');
183
+ if (!existsSync(built)) return false;
184
+
185
+ try {
186
+ copyFileSync(built, dest);
187
+ if (process.platform !== 'win32') chmodSync(dest, 0o755);
188
+ } catch {
189
+ return false;
190
+ }
191
+
192
+ return true;
193
+ }
package/bin/mdv.js CHANGED
@@ -3,55 +3,29 @@ import { existsSync } from 'node:fs';
3
3
  import { homedir } from 'node:os';
4
4
  import { join } from 'node:path';
5
5
  import { spawnSync } from 'node:child_process';
6
+ import { fileURLToPath } from 'node:url';
6
7
 
7
- const REPO = 'https://github.com/Dhruv2mars/mdv.git';
8
8
  const args = process.argv.slice(2);
9
- const envBin = process.env.MDV_BIN;
10
9
 
10
+ const envBin = process.env.MDV_BIN;
11
11
  if (envBin) run(envBin, args);
12
12
 
13
13
  const installRoot = process.env.MDV_INSTALL_ROOT || join(homedir(), '.mdv');
14
- const binName = process.platform === 'win32' ? 'mdv-cli.exe' : 'mdv-cli';
14
+ const binName = process.platform === 'win32' ? 'mdv.exe' : 'mdv';
15
15
  const installedBin = join(installRoot, 'bin', binName);
16
16
 
17
17
  if (!existsSync(installedBin)) {
18
- ensureCargo();
19
- console.error('mdv: installing rust binary (first run)...');
20
- const install = spawnSync(
21
- 'cargo',
22
- [
23
- 'install',
24
- 'mdv-cli',
25
- '--git',
26
- REPO,
27
- '--locked',
28
- '--root',
29
- installRoot,
30
- '--config',
31
- 'net.git-fetch-with-cli=true'
32
- ],
33
- {
34
- stdio: 'inherit',
35
- env: { ...process.env, CARGO_NET_GIT_FETCH_WITH_CLI: 'true' }
36
- }
37
- );
38
-
39
- if (install.status !== 0 || !existsSync(installedBin)) {
40
- console.error('mdv: install failed');
18
+ const here = fileURLToPath(new URL('.', import.meta.url));
19
+ const installer = join(here, 'install.js');
20
+ const res = spawnSync(process.execPath, [installer], { stdio: 'inherit', env: process.env });
21
+ if (res.status !== 0 || !existsSync(installedBin)) {
22
+ console.error('mdv: install missing. try reinstall: npm i -g @dhruv2mars/mdv');
41
23
  process.exit(1);
42
24
  }
43
25
  }
44
26
 
45
27
  run(installedBin, args);
46
28
 
47
- function ensureCargo() {
48
- const probe = spawnSync('cargo', ['--version'], { stdio: 'ignore' });
49
- if (probe.status === 0) return;
50
-
51
- console.error('mdv: rust toolchain not found. install rustup/cargo first.');
52
- process.exit(1);
53
- }
54
-
55
29
  function run(bin, binArgs) {
56
30
  const res = spawnSync(bin, binArgs, { stdio: 'inherit' });
57
31
  process.exit(res.status ?? 1);
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { mkdtempSync, rmSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { spawnSync } from 'node:child_process';
6
+
7
+ const root = mkdtempSync(join(tmpdir(), 'mdv-install-'));
8
+ const env = { ...process.env, MDV_INSTALL_ROOT: root };
9
+
10
+ const res = spawnSync(process.execPath, [new URL('./install.js', import.meta.url).pathname], {
11
+ stdio: 'inherit',
12
+ env
13
+ });
14
+
15
+ rmSync(root, { recursive: true, force: true });
16
+ process.exit(res.status ?? 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhruv2mars/mdv",
3
- "version": "0.0.2",
3
+ "version": "0.0.5",
4
4
  "description": "Terminal-first markdown visualizer/editor",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,7 +10,11 @@
10
10
  "bin"
11
11
  ],
12
12
  "scripts": {
13
- "mdv": "node bin/mdv.js"
13
+ "mdv": "node bin/mdv.js",
14
+ "postinstall": "node bin/install.js",
15
+ "selftest": "node bin/selftest.js",
16
+ "lint": "node --check bin/mdv.js && node --check bin/install.js && node --check bin/install-lib.js && node --check bin/selftest.js",
17
+ "test": "node scripts/test.js"
14
18
  },
15
19
  "keywords": [
16
20
  "markdown",
@@ -22,7 +26,8 @@
22
26
  "homepage": "https://github.com/Dhruv2mars/mdv#readme",
23
27
  "repository": {
24
28
  "type": "git",
25
- "url": "git+https://github.com/Dhruv2mars/mdv.git"
29
+ "url": "git+https://github.com/Dhruv2mars/mdv.git",
30
+ "directory": "packages/cli"
26
31
  },
27
32
  "bugs": {
28
33
  "url": "https://github.com/Dhruv2mars/mdv/issues"
@@ -31,5 +36,8 @@
31
36
  "node": ">=18"
32
37
  },
33
38
  "preferGlobal": true,
34
- "license": "MIT"
39
+ "license": "MIT",
40
+ "devDependencies": {
41
+ "@mdv/rust": "workspace:*"
42
+ }
35
43
  }