markmap-cli 0.15.0-alpha.0 → 0.15.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/bin/cli.js CHANGED
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const updateNotifier = require('update-notifier');
4
- const { main } = require('..');
5
- const pkg = require('../package.json');
3
+ import { main } from '../dist/index.js';
6
4
 
7
- const notifier = updateNotifier({ pkg });
8
- notifier.notify();
9
- main(pkg.version);
5
+ main().catch(err => {
6
+ console.error(err);
7
+ process.exitCode = 1;
8
+ });
package/dist/index.js CHANGED
@@ -1,19 +1,40 @@
1
- /*! markmap-cli v0.15.0-alpha.0 | MIT License */
2
- 'use strict';
1
+ /*! markmap-cli v0.15.0 | MIT License */
2
+ import { readFile, writeFile } from 'fs/promises';
3
+ import { fileURLToPath } from 'url';
4
+ import { Command } from 'commander';
5
+ import open from 'open';
6
+ import updateNotifier from 'update-notifier';
7
+ import { readPackageUp } from 'read-pkg-up';
8
+ import { buildCSSItem, buildJSItem, defer } from 'markmap-common';
9
+ import { Transformer, baseJsPaths } from 'markmap-lib';
10
+ export * from 'markmap-lib';
11
+ import { join, extname } from 'path';
12
+ import { createRequire } from 'module';
13
+ import { packageDirectory } from 'pkg-dir';
14
+ import { createReadStream } from 'fs';
15
+ import { EventEmitter } from 'events';
16
+ import http from 'http';
17
+ import Koa from 'koa';
18
+ import chokidar from 'chokidar';
3
19
 
4
- var fs = require('fs');
5
- var commander = require('commander');
6
- var open = require('open');
7
- var markmapLib = require('markmap-lib');
8
- var markmapCommon = require('markmap-common');
9
- var events = require('events');
10
- var http = require('http');
11
- var Koa = require('koa');
12
- var chokidar = require('chokidar');
20
+ function _extends() {
21
+ _extends = Object.assign ? Object.assign.bind() : function (target) {
22
+ for (var i = 1; i < arguments.length; i++) {
23
+ var source = arguments[i];
24
+ for (var key in source) {
25
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
26
+ target[key] = source[key];
27
+ }
28
+ }
29
+ }
30
+ return target;
31
+ };
32
+ return _extends.apply(this, arguments);
33
+ }
13
34
 
14
- const TOOLBAR_VERSION = "0.15.0-alpha.0";
15
- const TOOLBAR_CSS = `markmap-toolbar@${TOOLBAR_VERSION}/dist/style.min.css`;
16
- const TOOLBAR_JS = `markmap-toolbar@${TOOLBAR_VERSION}/dist/index.umd.min.js`;
35
+ const TOOLBAR_VERSION = "0.15.0";
36
+ const TOOLBAR_CSS = `markmap-toolbar@${TOOLBAR_VERSION}/dist/style.css`;
37
+ const TOOLBAR_JS = `markmap-toolbar@${TOOLBAR_VERSION}/dist/index.js`;
17
38
  const renderToolbar = () => {
18
39
  const {
19
40
  markmap,
@@ -25,10 +46,10 @@ const renderToolbar = () => {
25
46
  el.setAttribute('style', 'position:absolute;bottom:20px;right:20px');
26
47
  document.body.append(el);
27
48
  };
28
- function addToolbar(assets) {
49
+ function addToolbar(transformer, assets) {
29
50
  return {
30
- styles: [...(assets.styles || []), ...[TOOLBAR_CSS].map(markmapCommon.buildCSSItem)],
31
- scripts: [...(assets.scripts || []), ...[TOOLBAR_JS].map(markmapCommon.buildJSItem), {
51
+ styles: [...(assets.styles || []), ...[TOOLBAR_CSS].map(path => transformer.urlBuilder.getFullUrl(path)).map(path => buildCSSItem(path))],
52
+ scripts: [...(assets.scripts || []), ...[TOOLBAR_JS].map(path => transformer.urlBuilder.getFullUrl(path)).map(path => buildJSItem(path)), {
32
53
  type: 'iife',
33
54
  data: {
34
55
  fn: r => {
@@ -39,21 +60,46 @@ function addToolbar(assets) {
39
60
  }]
40
61
  };
41
62
  }
42
- class Defer {
43
- constructor() {
44
- this.promise = new Promise((resolve, reject) => {
45
- this.resolve = resolve;
46
- this.reject = reject;
47
- });
63
+ function removeVersionString(part) {
64
+ return part.replace(/@.+$/, '');
65
+ }
66
+ function localProvider(path) {
67
+ const parts = path.split('/');
68
+ // xxx@0.0.0-alpha.0+aaaaaa
69
+ // @scope/xxx@0.0.0-alpha.0+aaaaaa
70
+ if (parts[0].startsWith('@')) {
71
+ parts[1] = removeVersionString(parts[1]);
72
+ } else {
73
+ parts[0] = removeVersionString(parts[0]);
48
74
  }
75
+ path = parts.join('/');
76
+ return `/node_modules/${path}`;
77
+ }
78
+ const require = createRequire(import.meta.url);
79
+ async function doResolveFile(relpath) {
80
+ const parts = relpath.split('/');
81
+ const nameOffset = parts[0].startsWith('@') ? 2 : 1;
82
+ const name = parts.slice(0, nameOffset).join('/');
83
+ const filepath = parts.slice(nameOffset).join('/');
84
+ const mainPath = require.resolve(name);
85
+ const pkgDir = await packageDirectory({
86
+ cwd: mainPath
87
+ });
88
+ if (!pkgDir) throw new Error(`File not found: ${relpath}`);
89
+ const realpath = join(pkgDir, filepath);
90
+ return realpath;
91
+ }
92
+ const cache = {};
93
+ function resolveFile(relpath) {
94
+ cache[relpath] || (cache[relpath] = doResolveFile(relpath));
95
+ return cache[relpath];
49
96
  }
50
97
 
51
- /* eslint-disable max-classes-per-file */
52
98
  function sequence(fn) {
53
99
  let promise;
54
100
  return () => {
55
101
  promise || (promise = fn().finally(() => {
56
- promise = null;
102
+ promise = undefined;
57
103
  }));
58
104
  return promise;
59
105
  };
@@ -61,8 +107,10 @@ function sequence(fn) {
61
107
  class BufferContentProvider {
62
108
  constructor() {
63
109
  this.deferredSet = new Set();
64
- this.events = new events.EventEmitter();
110
+ this.events = new EventEmitter();
65
111
  this.ts = 0;
112
+ this.content = '';
113
+ this.line = -1;
66
114
  this.events.on('content', () => {
67
115
  this.feed({
68
116
  ts: this.ts,
@@ -76,7 +124,7 @@ class BufferContentProvider {
76
124
  });
77
125
  }
78
126
  async getUpdate(ts, timeout = 10000) {
79
- const deferred = new Defer();
127
+ const deferred = defer();
80
128
  this.deferredSet.add(deferred);
81
129
  setTimeout(() => {
82
130
  this.feed({}, deferred);
@@ -120,7 +168,7 @@ class FileSystemProvider extends BufferContentProvider {
120
168
  this.watcher = chokidar.watch(fileName).on('all', sequence(() => this.update()));
121
169
  }
122
170
  async update() {
123
- const content = await fs.promises.readFile(this.fileName, 'utf8');
171
+ const content = await readFile(this.fileName, 'utf8');
124
172
  this.setContent(content);
125
173
  }
126
174
  dispose() {
@@ -130,6 +178,9 @@ class FileSystemProvider extends BufferContentProvider {
130
178
  }
131
179
  function startServer(paddingBottom) {
132
180
  let ts = 0;
181
+ let root;
182
+ let line;
183
+ let offset = 0;
133
184
  const {
134
185
  mm,
135
186
  markmap
@@ -138,24 +189,48 @@ function startServer(paddingBottom) {
138
189
  function refresh() {
139
190
  fetch(`/data?ts=${ts}`).then(res => res.json()).then(res => {
140
191
  if (res.ts && res.ts > ts && res.result) {
141
- const {
192
+ var _frontmatter;
193
+ let frontmatter;
194
+ ({
142
195
  root,
143
196
  frontmatter,
144
- contentLineOffset
145
- } = res.result;
146
- mm.setOptions(markmap.deriveOptions(frontmatter == null ? void 0 : frontmatter.markmap));
197
+ contentLineOffset: offset
198
+ } = res.result);
199
+ mm.setOptions(markmap.deriveOptions((_frontmatter = frontmatter) == null ? void 0 : _frontmatter.markmap));
147
200
  mm.setData(root);
148
201
  if (!ts) mm.fit();
149
202
  ts = res.ts;
203
+ line = -1;
204
+ }
205
+ if (root && res.line != null && line !== res.line) {
206
+ line = res.line;
207
+ const active = findActiveNode();
208
+ if (active) mm.ensureView(active, {
209
+ bottom: paddingBottom
210
+ });
150
211
  }
151
212
  setTimeout(refresh, 300);
152
213
  });
153
214
  }
215
+ function findActiveNode() {
216
+ const lineWithoutFrontmatter = line - offset;
217
+ let best;
218
+ dfs(root);
219
+ return best;
220
+ function dfs(node) {
221
+ var _node$payload, _node$children;
222
+ const lines = (_node$payload = node.payload) == null ? void 0 : _node$payload.lines;
223
+ if (lines && lines[0] <= lineWithoutFrontmatter && lineWithoutFrontmatter < lines[1]) {
224
+ best = node;
225
+ }
226
+ (_node$children = node.children) == null ? void 0 : _node$children.forEach(dfs);
227
+ }
228
+ }
154
229
  }
155
230
  function setUpServer(transformer, provider, options) {
156
231
  let assets = transformer.getAssets();
157
- if (options.toolbar) assets = addToolbar(assets);
158
- const html = `${markmapLib.fillTemplate(null, assets)}<script>(${startServer.toString()})(${options.toolbar ? 60 : 0})</script>`;
232
+ if (options.toolbar) assets = addToolbar(transformer, assets);
233
+ const html = `${transformer.fillTemplate(null, assets)}<script>(${startServer.toString()})(${options.toolbar ? 60 : 0})</script>`;
159
234
  const app = new Koa();
160
235
  app.use(async (ctx, next) => {
161
236
  if (ctx.path === '/') {
@@ -169,6 +244,17 @@ function setUpServer(transformer, provider, options) {
169
244
  line: update.line
170
245
  };
171
246
  } else {
247
+ if (ctx.path.startsWith('/node_modules/')) {
248
+ const relpath = ctx.path.slice(14);
249
+ try {
250
+ const realpath = await resolveFile(relpath);
251
+ ctx.type = extname(relpath);
252
+ ctx.body = createReadStream(realpath);
253
+ return;
254
+ } catch (err) {
255
+ console.error(err);
256
+ }
257
+ }
172
258
  await next();
173
259
  }
174
260
  });
@@ -195,52 +281,99 @@ function setUpServer(transformer, provider, options) {
195
281
  };
196
282
  }
197
283
  async function develop(fileName, options) {
198
- const transformer = new markmapLib.Transformer();
284
+ const transformer = new Transformer();
285
+ transformer.urlBuilder.setProvider('local', localProvider);
286
+ transformer.urlBuilder.provider = 'local';
199
287
  const provider = fileName ? new FileSystemProvider(fileName) : new BufferContentProvider();
200
288
  return setUpServer(transformer, provider, options);
201
289
  }
202
290
 
291
+ async function loadFile(path) {
292
+ if (path.startsWith('/node_modules/')) {
293
+ const relpath = path.slice(14);
294
+ return readFile(await resolveFile(relpath), 'utf8');
295
+ }
296
+ const res = await fetch(path);
297
+ if (!res.ok) throw res;
298
+ return res.text();
299
+ }
300
+ async function inlineAssets(assets) {
301
+ const [scripts, styles] = await Promise.all([Promise.all((assets.scripts || []).map(async item => item.type === 'script' && item.data.src ? {
302
+ type: 'script',
303
+ data: {
304
+ textContent: await loadFile(item.data.src)
305
+ }
306
+ } : item)), Promise.all((assets.styles || []).map(async item => item.type === 'stylesheet' ? {
307
+ type: 'style',
308
+ data: await loadFile(item.data.href)
309
+ } : item))]);
310
+ return {
311
+ scripts,
312
+ styles
313
+ };
314
+ }
203
315
  async function createMarkmap(options) {
204
- const transformer = new markmapLib.Transformer();
316
+ const transformer = new Transformer();
317
+ if (options.offline) {
318
+ transformer.urlBuilder.setProvider('local', localProvider);
319
+ transformer.urlBuilder.provider = 'local';
320
+ } else {
321
+ await transformer.urlBuilder.findFastestProvider();
322
+ }
205
323
  const {
206
324
  root,
207
325
  features,
208
326
  frontmatter
209
327
  } = transformer.transform(options.content || '');
210
328
  let assets = transformer.getUsedAssets(features);
211
- if (options.toolbar) assets = addToolbar(assets);
212
- const html = markmapLib.fillTemplate(root, assets, {
329
+ assets = _extends({}, assets, {
330
+ scripts: [...baseJsPaths.map(path => transformer.urlBuilder.getFullUrl(path)).map(path => buildJSItem(path)), ...(assets.scripts || [])]
331
+ });
332
+ if (options.toolbar) {
333
+ assets = addToolbar(transformer, assets);
334
+ }
335
+ if (options.offline) {
336
+ assets = await inlineAssets(assets);
337
+ }
338
+ const html = transformer.fillTemplate(root, assets, {
339
+ baseJs: [],
213
340
  jsonOptions: frontmatter == null ? void 0 : frontmatter.markmap
214
341
  });
215
342
  const output = options.output || 'markmap.html';
216
- await fs.promises.writeFile(output, html, 'utf8');
343
+ await writeFile(output, html, 'utf8');
217
344
  if (options.open) open(output);
218
345
  }
219
- function main(version) {
220
- const program = new commander.Command();
221
- program.version(version).description('Create a markmap from a Markdown input file').arguments('<input>').option('--no-open', 'do not open the output file after generation').option('--no-toolbar', 'do not show toolbar').option('-o, --output <output>', 'specify filename of the output HTML').option('-w, --watch', 'watch the input file and update output on the fly, note that this feature is for development only').action(async (input, cmd) => {
222
- const content = await fs.promises.readFile(input, 'utf8');
346
+ async function main() {
347
+ var _await$readPackageUp;
348
+ const pkg = (_await$readPackageUp = await readPackageUp({
349
+ cwd: fileURLToPath(import.meta.url)
350
+ })) == null ? void 0 : _await$readPackageUp.packageJson;
351
+ if (!pkg) throw new Error('package.json not found');
352
+ const notifier = updateNotifier({
353
+ pkg
354
+ });
355
+ notifier.notify();
356
+ const program = new Command();
357
+ program.version(pkg.version).description('Create a markmap from a Markdown input file').arguments('<input>').option('--no-open', 'do not open the output file after generation').option('--no-toolbar', 'do not show toolbar').option('-o, --output <output>', 'specify filename of the output HTML').option('--offline', 'Inline all assets to allow the generated HTML to work offline').option('-w, --watch', 'watch the input file and update output on the fly, note that this feature is for development only').action(async (input, cmd) => {
358
+ const content = await readFile(input, 'utf8');
223
359
  const output = cmd.output || `${input.replace(/\.\w*$/, '')}.html`;
224
360
  if (cmd.watch) {
225
361
  await develop(input, {
226
362
  open: cmd.open,
227
- toolbar: cmd.toolbar
363
+ toolbar: cmd.toolbar,
364
+ offline: true
228
365
  });
229
366
  } else {
230
367
  await createMarkmap({
231
368
  content,
232
369
  output,
233
370
  open: cmd.open,
234
- toolbar: cmd.toolbar
371
+ toolbar: cmd.toolbar,
372
+ offline: cmd.offline
235
373
  });
236
374
  }
237
375
  });
238
376
  program.parse(process.argv);
239
377
  }
240
378
 
241
- exports.createMarkmap = createMarkmap;
242
- exports.develop = develop;
243
- exports.main = main;
244
- Object.keys(markmapLib).forEach(function (k) {
245
- if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = markmapLib[k];
246
- });
379
+ export { createMarkmap, develop, main };
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "markmap-cli",
3
- "version": "0.15.0-alpha.0",
3
+ "version": "0.15.0",
4
4
  "description": "Create markmaps from CLI",
5
5
  "author": "Gerald <gera2ld@live.com>",
6
6
  "license": "MIT",
7
7
  "bin": {
8
8
  "markmap": "bin/cli.js"
9
9
  },
10
+ "engines": {
11
+ "node": ">=16"
12
+ },
10
13
  "keywords": [
11
14
  "markmap",
12
15
  "markdown",
@@ -22,15 +25,17 @@
22
25
  "url": "git+https://github.com/markmap/markmap.git"
23
26
  },
24
27
  "scripts": {
25
- "dev": "rollup -wc rollup.conf.js",
28
+ "dev": "rollup -wc",
26
29
  "clean": "del-cli dist types",
27
30
  "build:types": "tsc",
28
- "build:js": "rollup -c rollup.conf.js"
31
+ "build:js": "rollup -c"
29
32
  },
30
33
  "bugs": {
31
34
  "url": "https://github.com/markmap/markmap/issues"
32
35
  },
36
+ "type": "module",
33
37
  "main": "dist/index.js",
38
+ "module": "dist/index.js",
34
39
  "files": [
35
40
  "bin",
36
41
  "dist",
@@ -38,18 +43,23 @@
38
43
  ],
39
44
  "typings": "types/index.d.ts",
40
45
  "dependencies": {
41
- "@babel/runtime": "^7.13.10",
42
- "chokidar": "^3.5.1",
43
- "commander": "^7.1.0",
44
- "koa": "^2.13.1",
45
- "markmap-common": "0.15.0-alpha.0",
46
- "markmap-lib": "0.15.0-alpha.0",
47
- "open": "^8.0.3",
48
- "update-notifier": "^5.1.0"
49
- },
50
- "devDependencies": {
51
- "markmap-toolbar": "0.15.0-alpha.0",
52
- "markmap-view": "0.15.0-alpha.0"
46
+ "@babel/runtime": "^7.22.6",
47
+ "chokidar": "^3.5.3",
48
+ "commander": "^11.0.0",
49
+ "d3": "^7.8.5",
50
+ "highlight.js": "^11.8.0",
51
+ "katex": "^0.16.8",
52
+ "koa": "^2.14.2",
53
+ "markmap-common": "0.15.0",
54
+ "markmap-lib": "0.15.0",
55
+ "markmap-toolbar": "0.15.0",
56
+ "markmap-view": "0.15.0",
57
+ "open": "^9.1.0",
58
+ "pkg-dir": "^7.0.0",
59
+ "prismjs": "^1.29.0",
60
+ "read-pkg-up": "^10.0.0",
61
+ "update-notifier": "^6.0.2",
62
+ "webfontloader": "^1.6.28"
53
63
  },
54
- "gitHead": "cea14d4791b8483481a99baa5d256017f52f267c"
64
+ "gitHead": "9c13bae5e28346ec613362046a023839ef64538b"
55
65
  }
package/types/index.d.ts CHANGED
@@ -4,4 +4,4 @@ import { develop } from './dev-server';
4
4
  export * from 'markmap-lib';
5
5
  export { develop };
6
6
  export declare function createMarkmap(options: IMarkmapCreateOptions & IDevelopOptions): Promise<void>;
7
- export declare function main(version: string): void;
7
+ export declare function main(): Promise<void>;
package/types/util.d.ts CHANGED
@@ -1,17 +1,19 @@
1
- import { IAssets } from 'markmap-lib';
1
+ import { IAssets, ITransformer } from 'markmap-lib';
2
2
  export interface IDevelopOptions {
3
3
  /**
4
- * whether to open the generated markmap in browser
4
+ * Whether to open the generated markmap in browser
5
5
  */
6
6
  open: boolean;
7
7
  /**
8
- * whether to show default toolbar
8
+ * Whether to show the default toolbar
9
9
  */
10
10
  toolbar: boolean;
11
+ /**
12
+ * Whether to inline all assets to make the HTML work offline.
13
+ * Ignored in watching mode.
14
+ */
15
+ offline: boolean;
11
16
  }
12
- export declare function addToolbar(assets: IAssets): IAssets;
13
- export declare class Defer<T> {
14
- resolve: (value: T) => void;
15
- reject: (err: Error) => void;
16
- promise: Promise<unknown>;
17
- }
17
+ export declare function addToolbar(transformer: ITransformer, assets: IAssets): IAssets;
18
+ export declare function localProvider(path: string): string;
19
+ export declare function resolveFile(relpath: string): Promise<string>;