@heroku-cli/plugin-devcenter 0.0.1 → 2.0.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.
Files changed (64) hide show
  1. package/LICENSE.txt +22 -0
  2. package/README.md +132 -17
  3. package/dist/commands/devcenter/open.d.ts +9 -0
  4. package/dist/commands/devcenter/open.d.ts.map +1 -0
  5. package/dist/commands/devcenter/open.js +33 -0
  6. package/dist/commands/devcenter/open.js.map +1 -0
  7. package/dist/commands/devcenter/preview.d.ts +14 -0
  8. package/dist/commands/devcenter/preview.d.ts.map +1 -0
  9. package/dist/commands/devcenter/preview.js +40 -0
  10. package/dist/commands/devcenter/preview.js.map +1 -0
  11. package/dist/commands/devcenter/pull.d.ts +12 -0
  12. package/dist/commands/devcenter/pull.d.ts.map +1 -0
  13. package/dist/commands/devcenter/pull.js +52 -0
  14. package/dist/commands/devcenter/pull.js.map +1 -0
  15. package/dist/commands/devcenter/push.d.ts +9 -0
  16. package/dist/commands/devcenter/push.d.ts.map +1 -0
  17. package/dist/commands/devcenter/push.js +105 -0
  18. package/dist/commands/devcenter/push.js.map +1 -0
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +2 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/lib/article-file.d.ts +21 -0
  24. package/dist/lib/article-file.d.ts.map +1 -0
  25. package/dist/lib/article-file.js +48 -0
  26. package/dist/lib/article-file.js.map +1 -0
  27. package/dist/lib/article-not-found.d.ts +3 -0
  28. package/dist/lib/article-not-found.d.ts.map +1 -0
  29. package/dist/lib/article-not-found.js +16 -0
  30. package/dist/lib/article-not-found.js.map +1 -0
  31. package/dist/lib/article-resolve.d.ts +23 -0
  32. package/dist/lib/article-resolve.d.ts.map +1 -0
  33. package/dist/lib/article-resolve.js +61 -0
  34. package/dist/lib/article-resolve.js.map +1 -0
  35. package/dist/lib/article-split.d.ts +9 -0
  36. package/dist/lib/article-split.d.ts.map +1 -0
  37. package/dist/lib/article-split.js +15 -0
  38. package/dist/lib/article-split.js.map +1 -0
  39. package/dist/lib/devcenter-client.d.ts +39 -0
  40. package/dist/lib/devcenter-client.d.ts.map +1 -0
  41. package/dist/lib/devcenter-client.js +119 -0
  42. package/dist/lib/devcenter-client.js.map +1 -0
  43. package/dist/lib/heroku-api-auth.d.ts +7 -0
  44. package/dist/lib/heroku-api-auth.d.ts.map +1 -0
  45. package/dist/lib/heroku-api-auth.js +12 -0
  46. package/dist/lib/heroku-api-auth.js.map +1 -0
  47. package/dist/lib/paths.d.ts +18 -0
  48. package/dist/lib/paths.d.ts.map +1 -0
  49. package/dist/lib/paths.js +48 -0
  50. package/dist/lib/paths.js.map +1 -0
  51. package/dist/lib/preview-server.d.ts +17 -0
  52. package/dist/lib/preview-server.d.ts.map +1 -0
  53. package/dist/lib/preview-server.js +94 -0
  54. package/dist/lib/preview-server.js.map +1 -0
  55. package/dist/lib/preview-templates.d.ts +7 -0
  56. package/dist/lib/preview-templates.d.ts.map +1 -0
  57. package/dist/lib/preview-templates.js +93 -0
  58. package/dist/lib/preview-templates.js.map +1 -0
  59. package/oclif.manifest.json +172 -1
  60. package/package.json +86 -47
  61. package/lib/commands/docs.d.ts +0 -25
  62. package/lib/commands/docs.js +0 -29
  63. package/lib/index.d.ts +0 -2
  64. package/lib/index.js +0 -3
package/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Heroku
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,31 +1,146 @@
1
- `heroku docs`
2
- ====
1
+ # @heroku-cli/plugin-devcenter
3
2
 
4
- CLI plugin to open the Heroku Dev Center
3
+ Heroku CLI plugin to interact with Heroku Dev Center
5
4
 
6
- <!-- toc -->
7
- * [Commands](#commands)
8
- <!-- tocstop -->
5
+ [![Version](https://img.shields.io/npm/v/@heroku-cli/plugin-devcenter.svg)](https://npmjs.org/package/@heroku-cli/plugin-devcenter)
6
+ [![License](https://img.shields.io/npm/l/@heroku-cli/plugin-devcenter.svg)](https://github.com/heroku/devcenter-cli/blob/main/LICENSE.txt)
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ heroku plugins:install @heroku-cli/plugin-devcenter
12
+ ```
13
+
14
+ <!-- usage -->
15
+ ```sh-session
16
+ $ npm install -g @heroku-cli/plugin-devcenter
17
+ $ heroku COMMAND
18
+ running command...
19
+ $ heroku (--version)
20
+ @heroku-cli/plugin-devcenter/2.0.0 darwin-arm64 node-v24.14.0
21
+ $ heroku --help [COMMAND]
22
+ USAGE
23
+ $ heroku COMMAND
24
+ ...
25
+ ```
26
+ <!-- usagestop -->
27
+
28
+ ## Commands
9
29
 
10
- # Commands
11
30
  <!-- commands -->
12
- * [`oclif-example docs [QUERY]`](#oclif-example-docs-query)
31
+ * [`heroku devcenter:open SLUG`](#heroku-devcenteropen-slug)
32
+ * [`heroku devcenter:preview SLUG`](#heroku-devcenterpreview-slug)
33
+ * [`heroku devcenter:pull SLUGORURL`](#heroku-devcenterpull-slugorurl)
34
+ * [`heroku devcenter:push SLUG`](#heroku-devcenterpush-slug)
35
+
36
+ ## `heroku devcenter:open SLUG`
37
+
38
+ open a Dev Center article in the browser (uses Heroku credentials for private or draft content when available)
39
+
40
+ ```
41
+ USAGE
42
+ $ heroku devcenter:open SLUG [--prompt]
43
+
44
+ ARGUMENTS
45
+ SLUG article slug (e.g. ps for https://devcenter.heroku.com/articles/ps)
13
46
 
14
- ## `oclif-example docs [QUERY]`
47
+ GLOBAL FLAGS
48
+ --prompt interactively prompt for command arguments and flags
15
49
 
16
- opens the Heroku Dev Center
50
+ DESCRIPTION
51
+ open a Dev Center article in the browser (uses Heroku credentials for private or draft content when available)
52
+ ```
53
+
54
+ _See code: [src/commands/devcenter/open.ts](https://github.com/heroku/devcenter-cli/blob/v2.0.0/src/commands/devcenter/open.ts)_
55
+
56
+ ## `heroku devcenter:preview SLUG`
57
+
58
+ preview a local Dev Center article in the browser with live reload
17
59
 
18
60
  ```
19
61
  USAGE
20
- $ oclif-example docs [QUERY]
62
+ $ heroku devcenter:preview SLUG [--prompt] [--host <value>] [--port <value>]
21
63
 
22
- OPTIONS
23
- -h, --help show CLI help
64
+ ARGUMENTS
65
+ SLUG article slug (local <slug>.md file)
24
66
 
25
- EXAMPLE
26
- $ heroku docs
27
- Opening Dev Center in your default browser...
67
+ FLAGS
68
+ --host=<value> [default: 127.0.0.1] bind host for the preview server
69
+ --port=<value> [default: 3000] port for the preview server
70
+
71
+ GLOBAL FLAGS
72
+ --prompt interactively prompt for command arguments and flags
73
+
74
+ DESCRIPTION
75
+ preview a local Dev Center article in the browser with live reload
28
76
  ```
29
77
 
30
- _See code: [src/commands/docs.ts](https://github.com/heroku/heroku-devcenter-plugin/blob/v0.0.1/src/commands/docs.ts)_
78
+ _See code: [src/commands/devcenter/preview.ts](https://github.com/heroku/devcenter-cli/blob/v2.0.0/src/commands/devcenter/preview.ts)_
79
+
80
+ ## `heroku devcenter:pull SLUGORURL`
81
+
82
+ save a local copy of a Dev Center article
83
+
84
+ ```
85
+ USAGE
86
+ $ heroku devcenter:pull SLUGORURL [--prompt] [-f]
87
+
88
+ ARGUMENTS
89
+ SLUGORURL article slug or full Dev Center article URL
90
+
91
+ FLAGS
92
+ -f, --force overwrite an existing local file without prompting
93
+
94
+ GLOBAL FLAGS
95
+ --prompt interactively prompt for command arguments and flags
96
+
97
+ DESCRIPTION
98
+ save a local copy of a Dev Center article
99
+ ```
100
+
101
+ _See code: [src/commands/devcenter/pull.ts](https://github.com/heroku/devcenter-cli/blob/v2.0.0/src/commands/devcenter/pull.ts)_
102
+
103
+ ## `heroku devcenter:push SLUG`
104
+
105
+ update a Dev Center article from a local markdown file
106
+
107
+ ```
108
+ USAGE
109
+ $ heroku devcenter:push SLUG [--prompt]
110
+
111
+ ARGUMENTS
112
+ SLUG article slug (optional .md suffix is ignored)
113
+
114
+ GLOBAL FLAGS
115
+ --prompt interactively prompt for command arguments and flags
116
+
117
+ DESCRIPTION
118
+ update a Dev Center article from a local markdown file
119
+ ```
120
+
121
+ _See code: [src/commands/devcenter/push.ts](https://github.com/heroku/devcenter-cli/blob/v2.0.0/src/commands/devcenter/push.ts)_
31
122
  <!-- commandsstop -->
123
+
124
+ ## Development
125
+
126
+ TypeScript code lives under `src/` with tests under `test/`. With Node 22+, run `npm install` and `npm test`.
127
+
128
+ If you have a Dev Center instance, you can point your CLI to it by setting the `DEVCENTER_BASE_URL` environment variable:
129
+
130
+ ```bash
131
+ export DEVCENTER_BASE_URL=http://localhost:3000
132
+ ```
133
+
134
+ Verbose logging uses the [`debug`](https://www.npmjs.com/package/debug) package:
135
+
136
+ ```bash
137
+ DEBUG=devcenter:open heroku devcenter:open my-article
138
+ DEBUG=devcenter:preview heroku devcenter:preview my-article
139
+ DEBUG=devcenter:* heroku devcenter:open my-article
140
+ ```
141
+
142
+ ## License
143
+
144
+ See [LICENSE.txt](LICENSE.txt) file.
145
+
146
+ The `preview` command uses the [Font Awesome](http://fontawesome.io/) vector icons, which have their own [License](https://github.com/FortAwesome/Font-Awesome#license).
@@ -0,0 +1,9 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ export default class Open extends Command {
3
+ static args: {
4
+ slug: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ run(): Promise<void>;
8
+ }
9
+ //# sourceMappingURL=open.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../../../src/commands/devcenter/open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,qBAAqB,CAAA;AAY3C,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,OAAO;IACvC,MAAM,CAAC,IAAI;;MAKV;IACD,MAAM,CAAC,WAAW,SACkG;IAE9G,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAiB3B"}
@@ -0,0 +1,33 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ import { Args } from '@oclif/core';
3
+ import createDebug from 'debug';
4
+ import open from 'open';
5
+ import { formatArticleNotFoundMessage } from '../../lib/article-not-found.js';
6
+ import { fetchArticleJsonForSlug } from '../../lib/article-resolve.js';
7
+ import { DevcenterClient } from '../../lib/devcenter-client.js';
8
+ import { articlePath, getDevcenterBaseUrl } from '../../lib/paths.js';
9
+ const dbg = createDebug('devcenter:open');
10
+ export default class Open extends Command {
11
+ static args = {
12
+ slug: Args.string({
13
+ description: 'article slug (e.g. ps for https://devcenter.heroku.com/articles/ps)',
14
+ required: true,
15
+ }),
16
+ };
17
+ static description = 'open a Dev Center article in the browser (uses Heroku credentials for private or draft content when available)';
18
+ async run() {
19
+ const { args } = await this.parse(Open);
20
+ const slug = args.slug.trim();
21
+ const client = new DevcenterClient();
22
+ dbg(`baseUrl=${getDevcenterBaseUrl()} path=${articlePath(slug)} expectedSlug=${slug}`);
23
+ const token = await this.heroku.getAuth();
24
+ const article = await fetchArticleJsonForSlug(client, slug, token, dbg);
25
+ if (!article) {
26
+ const msg = await formatArticleNotFoundMessage(client, slug);
27
+ this.error(msg, { exit: 1 });
28
+ }
29
+ dbg('Article found, opening');
30
+ await open(`${getDevcenterBaseUrl()}${articlePath(slug)}`);
31
+ }
32
+ }
33
+ //# sourceMappingURL=open.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open.js","sourceRoot":"","sources":["../../../src/commands/devcenter/open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,qBAAqB,CAAA;AAC3C,OAAO,EAAC,IAAI,EAAC,MAAM,aAAa,CAAA;AAChC,OAAO,WAAW,MAAM,OAAO,CAAA;AAC/B,OAAO,IAAI,MAAM,MAAM,CAAA;AAEvB,OAAO,EAAC,4BAA4B,EAAC,MAAM,gCAAgC,CAAA;AAC3E,OAAO,EAAC,uBAAuB,EAAC,MAAM,8BAA8B,CAAA;AACpE,OAAO,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAA;AAC7D,OAAO,EAAC,WAAW,EAAE,mBAAmB,EAAC,MAAM,oBAAoB,CAAA;AAEnE,MAAM,GAAG,GAAG,WAAW,CAAC,gBAAgB,CAAC,CAAA;AAEzC,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,OAAO;IACvC,MAAM,CAAC,IAAI,GAAG;QACZ,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;YAChB,WAAW,EAAE,qEAAqE;YAClF,QAAQ,EAAE,IAAI;SACf,CAAC;KACH,CAAA;IACD,MAAM,CAAC,WAAW,GACd,gHAAgH,CAAA;IAEpH,KAAK,CAAC,GAAG;QACP,MAAM,EAAC,IAAI,EAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;QAC7B,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAA;QAEpC,GAAG,CAAC,WAAW,mBAAmB,EAAE,SAAS,WAAW,CAAC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAA;QAEtF,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAA;QACzC,MAAM,OAAO,GAAG,MAAM,uBAAuB,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAAA;QACvE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,MAAM,4BAA4B,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;YAC5D,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,EAAC,IAAI,EAAE,CAAC,EAAC,CAAC,CAAA;QAC5B,CAAC;QAED,GAAG,CAAC,wBAAwB,CAAC,CAAA;QAC7B,MAAM,IAAI,CAAC,GAAG,mBAAmB,EAAE,GAAG,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC5D,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ export default class Preview extends Command {
3
+ static args: {
4
+ slug: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static flags: {
8
+ host: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ port: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
10
+ };
11
+ static id: string;
12
+ run(): Promise<void>;
13
+ }
14
+ //# sourceMappingURL=preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../../src/commands/devcenter/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,qBAAqB,CAAA;AAO3C,MAAM,CAAC,OAAO,OAAO,OAAQ,SAAQ,OAAO;IAC1C,MAAM,CAAC,IAAI;;MAKV;IACD,MAAM,CAAC,WAAW,SAAuE;IACzF,MAAM,CAAC,KAAK;;;MASX;IACD,MAAM,CAAC,EAAE,SAAsB;IAEzB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAe3B"}
@@ -0,0 +1,40 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ import { Args, Flags } from '@oclif/core';
3
+ import { existsSync } from 'node:fs';
4
+ import { mdFilePath } from '../../lib/paths.js';
5
+ import { runPreview } from '../../lib/preview-server.js';
6
+ export default class Preview extends Command {
7
+ static args = {
8
+ slug: Args.string({
9
+ description: 'article slug (local <slug>.md file)',
10
+ required: true,
11
+ }),
12
+ };
13
+ static description = 'preview a local Dev Center article in the browser with live reload';
14
+ static flags = {
15
+ host: Flags.string({
16
+ default: '127.0.0.1',
17
+ description: 'bind host for the preview server',
18
+ }),
19
+ port: Flags.integer({
20
+ default: 3000,
21
+ description: 'port for the preview server',
22
+ }),
23
+ };
24
+ static id = 'devcenter:preview';
25
+ async run() {
26
+ const { args, flags } = await this.parse(Preview);
27
+ const slug = args.slug.replace(/\.md$/i, '').trim();
28
+ const mdPath = mdFilePath(slug);
29
+ if (!existsSync(mdPath)) {
30
+ this.error(`Can't find ${mdPath} file - you may want to \`heroku devcenter:pull ${slug}\``, { exit: 1 });
31
+ }
32
+ await runPreview({
33
+ host: flags.host,
34
+ mdPath,
35
+ port: flags.port,
36
+ slug,
37
+ });
38
+ }
39
+ }
40
+ //# sourceMappingURL=preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.js","sourceRoot":"","sources":["../../../src/commands/devcenter/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,qBAAqB,CAAA;AAC3C,OAAO,EAAC,IAAI,EAAE,KAAK,EAAC,MAAM,aAAa,CAAA;AACvC,OAAO,EAAC,UAAU,EAAC,MAAM,SAAS,CAAA;AAElC,OAAO,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAA;AAC7C,OAAO,EAAC,UAAU,EAAC,MAAM,6BAA6B,CAAA;AAEtD,MAAM,CAAC,OAAO,OAAO,OAAQ,SAAQ,OAAO;IAC1C,MAAM,CAAC,IAAI,GAAG;QACZ,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;YAChB,WAAW,EAAE,qCAAqC;YAClD,QAAQ,EAAE,IAAI;SACf,CAAC;KACH,CAAA;IACD,MAAM,CAAC,WAAW,GAAG,oEAAoE,CAAA;IACzF,MAAM,CAAC,KAAK,GAAG;QACb,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC;YACjB,OAAO,EAAE,WAAW;YACpB,WAAW,EAAE,kCAAkC;SAChD,CAAC;QACF,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC;YAClB,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,6BAA6B;SAC3C,CAAC;KACH,CAAA;IACD,MAAM,CAAC,EAAE,GAAG,mBAAmB,CAAA;IAE/B,KAAK,CAAC,GAAG;QACP,MAAM,EAAC,IAAI,EAAE,KAAK,EAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QACnD,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;QAC/B,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,KAAK,CAAC,cAAc,MAAM,mDAAmD,IAAI,IAAI,EAAE,EAAC,IAAI,EAAE,CAAC,EAAC,CAAC,CAAA;QACxG,CAAC;QAED,MAAM,UAAU,CAAC;YACf,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,MAAM;YACN,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI;SACL,CAAC,CAAA;IACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ export default class Pull extends Command {
3
+ static args: {
4
+ slugOrUrl: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static flags: {
8
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ run(): Promise<void>;
11
+ }
12
+ //# sourceMappingURL=pull.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../../src/commands/devcenter/pull.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,qBAAqB,CAAA;AAmB3C,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,OAAO;IACvC,MAAM,CAAC,IAAI;;MAKV;IACD,MAAM,CAAC,WAAW,SAA8C;IAChE,MAAM,CAAC,KAAK;;MAKX;IAEK,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA6B3B"}
@@ -0,0 +1,52 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ import { confirm } from '@heroku/heroku-cli-util/hux';
3
+ import { Args, Flags } from '@oclif/core';
4
+ import createDebug from 'debug';
5
+ import { existsSync, writeFileSync } from 'node:fs';
6
+ import { stringify as stringifyYaml } from 'yaml';
7
+ import { formatArticleNotFoundMessage } from '../../lib/article-not-found.js';
8
+ import { fetchArticleJsonForSlug } from '../../lib/article-resolve.js';
9
+ import { DevcenterClient } from '../../lib/devcenter-client.js';
10
+ import { articleApiPath, getDevcenterBaseUrl, mdFilePath, slugFromArticleUrl, } from '../../lib/paths.js';
11
+ const dbg = createDebug('devcenter:pull');
12
+ export default class Pull extends Command {
13
+ static args = {
14
+ slugOrUrl: Args.string({
15
+ description: 'article slug or full Dev Center article URL',
16
+ required: true,
17
+ }),
18
+ };
19
+ static description = 'save a local copy of a Dev Center article';
20
+ static flags = {
21
+ force: Flags.boolean({
22
+ char: 'f',
23
+ description: 'overwrite an existing local file without prompting',
24
+ }),
25
+ };
26
+ async run() {
27
+ const { args, flags } = await this.parse(Pull);
28
+ const raw = args.slugOrUrl.trim();
29
+ const slug = slugFromArticleUrl(raw).trim();
30
+ const client = new DevcenterClient();
31
+ dbg(`baseUrl=${getDevcenterBaseUrl()} path=${articleApiPath(slug)} expectedSlug=${slug}`);
32
+ const token = await this.heroku.getAuth();
33
+ const article = await fetchArticleJsonForSlug(client, slug, token, dbg);
34
+ if (!article) {
35
+ const msg = await formatArticleNotFoundMessage(client, slug);
36
+ this.error(msg, { exit: 1 });
37
+ }
38
+ const metadata = { id: article.id, title: article.title };
39
+ const filePath = mdFilePath(slug);
40
+ if (!flags.force && existsSync(filePath)) {
41
+ const shouldOverwrite = await confirm(`The file ${filePath} already exists - overwrite?`);
42
+ if (!shouldOverwrite) {
43
+ return;
44
+ }
45
+ }
46
+ const yamlBlock = stringifyYaml(metadata).trimEnd();
47
+ const fileContent = `${yamlBlock}\n\n${article.content}`;
48
+ writeFileSync(filePath, fileContent, 'utf8');
49
+ this.log(`"${metadata.title}" article saved as ${filePath}`);
50
+ }
51
+ }
52
+ //# sourceMappingURL=pull.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.js","sourceRoot":"","sources":["../../../src/commands/devcenter/pull.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ export default class Push extends Command {
3
+ static args: {
4
+ slug: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ run(): Promise<void>;
8
+ }
9
+ //# sourceMappingURL=push.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../src/commands/devcenter/push.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,qBAAqB,CAAA;AAmC3C,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,OAAO;IACvC,MAAM,CAAC,IAAI;;MAKV;IACD,MAAM,CAAC,WAAW,SAA2D;IAEvE,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA4D3B"}
@@ -0,0 +1,105 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ import { Args } from '@oclif/core';
3
+ import createDebug from 'debug';
4
+ import { existsSync } from 'node:fs';
5
+ import { stringify as stringifyYaml } from 'yaml';
6
+ import { ArticleFile } from '../../lib/article-file.js';
7
+ import { DevcenterClient } from '../../lib/devcenter-client.js';
8
+ import { mdFilePath } from '../../lib/paths.js';
9
+ const dbg = createDebug('devcenter:push');
10
+ function hasValidationErrors(body) {
11
+ if (body === undefined || body === null) {
12
+ return false;
13
+ }
14
+ if (Array.isArray(body)) {
15
+ return body.length > 0;
16
+ }
17
+ if (typeof body === 'object') {
18
+ return Object.keys(body).length > 0;
19
+ }
20
+ return false;
21
+ }
22
+ export default class Push extends Command {
23
+ static args = {
24
+ slug: Args.string({
25
+ description: 'article slug (optional .md suffix is ignored)',
26
+ required: true,
27
+ }),
28
+ };
29
+ static description = 'update a Dev Center article from a local markdown file';
30
+ async run() {
31
+ const { args } = await this.parse(Push);
32
+ const slug = args.slug.replace(/\.md$/i, '').trim();
33
+ const mdPath = mdFilePath(slug);
34
+ if (!existsSync(mdPath)) {
35
+ this.error(`Can't find ${mdPath} file - you may want to \`heroku devcenter:pull ${slug}\``, { exit: 1 });
36
+ }
37
+ const article = ArticleFile.read(mdPath);
38
+ if (article.parsingError) {
39
+ this.error(`The content of ${mdPath} can't be parsed properly, fix it and try again.`, { exit: 1 });
40
+ }
41
+ const token = await this.heroku.getAuth();
42
+ if (!token) {
43
+ this.error('Heroku credentials not found. Run `heroku login`.', { exit: 1 });
44
+ }
45
+ const client = new DevcenterClient();
46
+ const formParams = {
47
+ 'article[content]': article.content,
48
+ 'article[title]': String(article.metadata.title),
49
+ };
50
+ dbg(`Pushing article id=${article.metadata.id} title="${article.metadata.title}"`);
51
+ const broken = await client.checkBrokenLinks(token, article.content);
52
+ dbg(`Broken link check: ${Array.isArray(broken.body) ? broken.body.length : 0} issues`);
53
+ const links = broken.body;
54
+ if (Array.isArray(links) && links.length > 0) {
55
+ this.log(`The article "${slug}" contains broken link/s:`);
56
+ for (const link of links) {
57
+ this.log(`- [${link.text}](${link.url})`);
58
+ }
59
+ this.log('');
60
+ }
61
+ const validated = await client.validateArticle(token, article.metadata.id, formParams);
62
+ dbg(`Validation response: status=${validated.status} ok=${validated.ok}`);
63
+ if (hasValidationErrors(validated.body)) {
64
+ const dumped = stringifyYaml(validated.body);
65
+ this.error(`The article "${slug}" can't be saved:\n${dumped}`, { exit: 1 });
66
+ }
67
+ const updated = await client.updateArticle(token, article.metadata.id, formParams);
68
+ dbg(`Update response: status=${updated.status} ok=${updated.ok}`);
69
+ if (!updated.ok) {
70
+ const errBody = updated.body;
71
+ this.error(`Error pushing "${slug}": ${errBody.error ?? updated.status}`, { exit: 1 });
72
+ }
73
+ const result = updated.body;
74
+ const verb = statusVerb(result.status);
75
+ if (verb && result.title && result.url) {
76
+ this.log(`Article "${result.title}" ${verb} to ${result.url}`);
77
+ }
78
+ else {
79
+ this.log('Article update completed.');
80
+ }
81
+ }
82
+ }
83
+ function statusVerb(status) {
84
+ switch (status) {
85
+ case 'archived': {
86
+ return 'archived';
87
+ }
88
+ case 'draft': {
89
+ return 'pushed in draft mode';
90
+ }
91
+ case 'published': {
92
+ return 'published';
93
+ }
94
+ case 'published_quietly': {
95
+ return 'published quietly';
96
+ }
97
+ case 'staging': {
98
+ return 'pushed as staging mode';
99
+ }
100
+ default: {
101
+ return undefined;
102
+ }
103
+ }
104
+ }
105
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../../src/commands/devcenter/push.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { run } from '@oclif/core';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,aAAa,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { run } from '@oclif/core';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,aAAa,CAAA"}
@@ -0,0 +1,21 @@
1
+ export type ArticleMetadata = {
2
+ id: number | string;
3
+ title: string;
4
+ };
5
+ export type TocEntry = {
6
+ id: string;
7
+ text: string;
8
+ };
9
+ export declare class ArticleFile {
10
+ content: string;
11
+ html: string;
12
+ metadata: ArticleMetadata;
13
+ parsingError?: string;
14
+ toc: TocEntry[];
15
+ constructor(opts: {
16
+ content?: string;
17
+ metadata?: Partial<ArticleMetadata>;
18
+ });
19
+ static read(srcPath: string): ArticleFile;
20
+ }
21
+ //# sourceMappingURL=article-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article-file.d.ts","sourceRoot":"","sources":["../../src/lib/article-file.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAKD,MAAM,MAAM,QAAQ,GAAG;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,CAAA;AAEjD,qBAAa,WAAW;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,eAAe,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,GAAG,EAAE,QAAQ,EAAE,CAAA;gBAEH,IAAI,EAAE;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;KAAC;IA0B1E,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW;CAM1C"}
@@ -0,0 +1,48 @@
1
+ import * as cheerio from 'cheerio';
2
+ import MarkdownIt from 'markdown-it';
3
+ import anchor from 'markdown-it-anchor';
4
+ import { readFileSync } from 'node:fs';
5
+ import { parse as parseYaml } from 'yaml';
6
+ import { splitMetadataBody } from './article-split.js';
7
+ const md = new MarkdownIt({ html: false, linkify: true, typographer: true });
8
+ md.use(anchor, { permalink: false });
9
+ export class ArticleFile {
10
+ content;
11
+ html;
12
+ metadata;
13
+ parsingError;
14
+ toc;
15
+ constructor(opts) {
16
+ this.metadata = opts.metadata;
17
+ this.content = opts.content ?? '';
18
+ try {
19
+ this.html = md.render(this.content);
20
+ }
21
+ catch (error) {
22
+ this.parsingError = error instanceof Error ? error.message : String(error);
23
+ this.html = '';
24
+ }
25
+ if (this.html) {
26
+ const $ = cheerio.load(this.html);
27
+ this.toc = $('h2')
28
+ .toArray()
29
+ .map(el => {
30
+ const $el = $(el);
31
+ return {
32
+ id: $el.attr('id') ?? '',
33
+ text: $el.text(),
34
+ };
35
+ });
36
+ }
37
+ else {
38
+ this.toc = [];
39
+ }
40
+ }
41
+ static read(srcPath) {
42
+ const src = readFileSync(srcPath, 'utf8');
43
+ const { body, yamlText } = splitMetadataBody(src);
44
+ const metadata = parseYaml(yamlText);
45
+ return new ArticleFile({ content: body, metadata });
46
+ }
47
+ }
48
+ //# sourceMappingURL=article-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article-file.js","sourceRoot":"","sources":["../../src/lib/article-file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,UAAU,MAAM,aAAa,CAAA;AACpC,OAAO,MAAM,MAAM,oBAAoB,CAAA;AACvC,OAAO,EAAC,YAAY,EAAC,MAAM,SAAS,CAAA;AACpC,OAAO,EAAC,KAAK,IAAI,SAAS,EAAC,MAAM,MAAM,CAAA;AAEvC,OAAO,EAAC,iBAAiB,EAAC,MAAM,oBAAoB,CAAA;AAOpD,MAAM,EAAE,GAAG,IAAI,UAAU,CAAC,EAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAC,CAAC,CAAA;AAC1E,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,EAAC,SAAS,EAAE,KAAK,EAAC,CAAC,CAAA;AAIlC,MAAM,OAAO,WAAW;IACtB,OAAO,CAAQ;IACf,IAAI,CAAQ;IACZ,QAAQ,CAAiB;IACzB,YAAY,CAAS;IACrB,GAAG,CAAY;IAEf,YAAY,IAA8D;QACxE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAA2B,CAAA;QAChD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,CAAA;QACjC,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAC1E,IAAI,CAAC,IAAI,GAAG,EAAE,CAAA;QAChB,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACjC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC;iBACf,OAAO,EAAE;iBACT,GAAG,CAAC,EAAE,CAAC,EAAE;gBACR,MAAM,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,CAAA;gBACjB,OAAO;oBACL,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;oBACxB,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE;iBACjB,CAAA;YACH,CAAC,CAAC,CAAA;QACN,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,GAAG,EAAE,CAAA;QACf,CAAC;IACH,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,OAAe;QACzB,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QACzC,MAAM,EAAC,IAAI,EAAE,QAAQ,EAAC,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAA;QAC/C,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAoB,CAAA;QACvD,OAAO,IAAI,WAAW,CAAC,EAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAC,CAAC,CAAA;IACnD,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import type { DevcenterClient } from './devcenter-client.js';
2
+ export declare function formatArticleNotFoundMessage(client: DevcenterClient, slug: string): Promise<string>;
3
+ //# sourceMappingURL=article-not-found.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article-not-found.d.ts","sourceRoot":"","sources":["../../src/lib/article-not-found.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,uBAAuB,CAAA;AAM1D,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CAcjB"}
@@ -0,0 +1,16 @@
1
+ import { articleUrlMatches, getDevcenterBaseUrl, searchApiPath } from './paths.js';
2
+ export async function formatArticleNotFoundMessage(client, slug) {
3
+ const baseUrl = getDevcenterBaseUrl();
4
+ const { body } = await client.getJson(searchApiPath(), { query: slug });
5
+ const results = (body?.results ?? []).filter(r => articleUrlMatches(r.full_url, baseUrl));
6
+ const lines = [`No ${slug} article found.`];
7
+ if (results.length > 0) {
8
+ lines.push('Perhaps you meant one of these:');
9
+ const longest = Math.max(...results.map(r => r.slug.length));
10
+ for (const s of results) {
11
+ lines.push(`${s.slug.padEnd(longest)} # ${s.title}`);
12
+ }
13
+ }
14
+ return lines.join('\n');
15
+ }
16
+ //# sourceMappingURL=article-not-found.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article-not-found.js","sourceRoot":"","sources":["../../src/lib/article-not-found.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,iBAAiB,EAAE,mBAAmB,EAAE,aAAa,EAAC,MAAM,YAAY,CAAA;AAIhF,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,MAAuB,EACvB,IAAY;IAEZ,MAAM,OAAO,GAAG,mBAAmB,EAAE,CAAA;IACrC,MAAM,EAAC,IAAI,EAAC,GAAG,MAAM,MAAM,CAAC,OAAO,CAA4B,aAAa,EAAE,EAAE,EAAC,KAAK,EAAE,IAAI,EAAC,CAAC,CAAA;IAC9F,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAA;IACzF,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,iBAAiB,CAAC,CAAA;IAC3C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAA;QAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;QAC5D,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC,CAAA;QACtD,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}