@sillsdev/docu-notion 0.12.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 (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +135 -0
  3. package/dist/FlatGuidLayoutStrategy.d.ts +6 -0
  4. package/dist/FlatGuidLayoutStrategy.js +25 -0
  5. package/dist/HierarchicalNamedLayoutStrategy.d.ts +7 -0
  6. package/dist/HierarchicalNamedLayoutStrategy.js +80 -0
  7. package/dist/LayoutStrategy.d.ts +12 -0
  8. package/dist/LayoutStrategy.js +83 -0
  9. package/dist/MakeImagePersistencePlan.d.ts +2 -0
  10. package/dist/MakeImagePersistencePlan.js +66 -0
  11. package/dist/NotionImage-CaptionReading.spec.d.ts +1 -0
  12. package/dist/NotionImage-CaptionReading.spec.js +233 -0
  13. package/dist/NotionPage.d.ts +44 -0
  14. package/dist/NotionPage.js +194 -0
  15. package/dist/config/configuration.d.ts +5 -0
  16. package/dist/config/configuration.js +86 -0
  17. package/dist/config/default.docunotion.config.d.ts +3 -0
  18. package/dist/config/default.docunotion.config.js +37 -0
  19. package/dist/images.d.ts +24 -0
  20. package/dist/images.js +230 -0
  21. package/dist/index.d.ts +7 -0
  22. package/dist/index.js +37 -0
  23. package/dist/log.d.ts +11 -0
  24. package/dist/log.js +61 -0
  25. package/dist/makeImagePersistencePlan.spec.d.ts +1 -0
  26. package/dist/makeImagePersistencePlan.spec.js +35 -0
  27. package/dist/notion-styles.css +58 -0
  28. package/dist/plugins/CalloutTransformer.d.ts +24 -0
  29. package/dist/plugins/CalloutTransformer.js +88 -0
  30. package/dist/plugins/CalloutTransformer.spec.d.ts +1 -0
  31. package/dist/plugins/CalloutTransformer.spec.js +199 -0
  32. package/dist/plugins/ColumnListTransformer.d.ts +2 -0
  33. package/dist/plugins/ColumnListTransformer.js +34 -0
  34. package/dist/plugins/ColumnTransformer.d.ts +2 -0
  35. package/dist/plugins/ColumnTransformer.js +67 -0
  36. package/dist/plugins/EscapeHtmlBlockModifier.d.ts +2 -0
  37. package/dist/plugins/EscapeHtmlBlockModifier.js +41 -0
  38. package/dist/plugins/EscapeHtmlBlockModifier.spec.d.ts +1 -0
  39. package/dist/plugins/EscapeHtmlBlockModifier.spec.js +130 -0
  40. package/dist/plugins/HeadingTranformer.spec.d.ts +1 -0
  41. package/dist/plugins/HeadingTranformer.spec.js +46 -0
  42. package/dist/plugins/HeadingTransformer.d.ts +2 -0
  43. package/dist/plugins/HeadingTransformer.js +63 -0
  44. package/dist/plugins/NumberedListTransformer.d.ts +2 -0
  45. package/dist/plugins/NumberedListTransformer.js +55 -0
  46. package/dist/plugins/NumberedListTransformer.spec.d.ts +1 -0
  47. package/dist/plugins/NumberedListTransformer.spec.js +86 -0
  48. package/dist/plugins/TableTransformer.d.ts +5 -0
  49. package/dist/plugins/TableTransformer.js +70 -0
  50. package/dist/plugins/embedTweaks.d.ts +5 -0
  51. package/dist/plugins/embedTweaks.js +46 -0
  52. package/dist/plugins/embedTweaks.spec.d.ts +1 -0
  53. package/dist/plugins/embedTweaks.spec.js +230 -0
  54. package/dist/plugins/externalLinks.d.ts +2 -0
  55. package/dist/plugins/externalLinks.js +26 -0
  56. package/dist/plugins/externalLinks.spec.d.ts +1 -0
  57. package/dist/plugins/externalLinks.spec.js +132 -0
  58. package/dist/plugins/internalLinks.d.ts +6 -0
  59. package/dist/plugins/internalLinks.js +78 -0
  60. package/dist/plugins/internalLinks.spec.d.ts +1 -0
  61. package/dist/plugins/internalLinks.spec.js +442 -0
  62. package/dist/plugins/pluginTestRun.d.ts +10 -0
  63. package/dist/plugins/pluginTestRun.js +248 -0
  64. package/dist/plugins/pluginTypes.d.ts +42 -0
  65. package/dist/plugins/pluginTypes.js +2 -0
  66. package/dist/pull.d.ts +12 -0
  67. package/dist/pull.js +253 -0
  68. package/dist/run.d.ts +1 -0
  69. package/dist/run.js +35 -0
  70. package/dist/transform.d.ts +6 -0
  71. package/dist/transform.js +195 -0
  72. package/dist/types.d.ts +8 -0
  73. package/dist/types.js +2 -0
  74. package/package.json +96 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 SIL International
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # docu-notion
2
+
3
+ docu-notion lets you use Notion as your editor for [Docusaurus](https://docusaurus.io/). Using Notion instead of raw markdown files means that you don't have to teach non-developers how to make git commits and pull requests. It also allows you to leverage Notion's database tools to control workflow, Notion's commenting feature to discuss changes, etc.
4
+
5
+ Example Site: https://sillsdev.github.io/docu-notion-sample-site/
6
+
7
+ # Instructions
8
+
9
+ ## 1. Set up your documentation site.
10
+
11
+ First, prepare your markdown-based static file system like [Docusaurus](https://docusaurus.io/). For a shortcut with github actions, search, and deployment to github pages, you can just copy [this template](https://github.com/sillsdev/docu-notion-sample-site).
12
+
13
+ ## 2. In Notion, duplicate the docu-notion template
14
+
15
+ Go to [this template page](https://hattonjohn.notion.site/Documentation-Template-Docusaurus-0e998b32da3c47edad0f62a25b49818c). Duplicate it into your own workspace.
16
+ You can name it anything you like, e.g. "Documentation Root".
17
+
18
+ ## 3. Create a Notion Integration
19
+
20
+ In order for docu-notion to read your site via Notion's API, you need to create what Notion calls an "integration". Follow [these instructions](https://developers.notion.com/docs/getting-started) to make an integration and get your token. Limit your integration to "READ" access.
21
+
22
+ ## 4. "Invite" your Notion Integration to read you page
23
+
24
+ In Notion, click "Share" on the root of your documentation and "invite" your integration to it.
25
+
26
+ ![image](https://user-images.githubusercontent.com/8448/168930238-1dcf46df-a690-4839-bf4c-c63157f104d8.png)
27
+
28
+ ## 5. Add your pages under your Outline page.
29
+
30
+ Currently, docu-notion expects that each page has only one of the following: sub-pages, links to other pages, or normal content. Do not mix them. You can add content pages directly here, but then you won't be able to make use of the workflow features. If those matter to you, instead make new pages under the "Database" and then link to them in your outline pages.
31
+
32
+ ## 6. Pull your pages
33
+
34
+ First, determine the id of your root page by clicking "Share" and looking at the the url it gives you. E.g.
35
+ https://www.notion.so/hattonjohn/My-Docs-0456aa5842946bdbea3a4f37c97a0e5
36
+ means that the id is "0456aa5842946PRETEND4f37c97a0e5".
37
+
38
+ Determine where you want the markdown files and images to land. The following works well for Docusaurus instances:
39
+
40
+ ```
41
+ npx docu-notion -n secret_PRETEND123456789PRETEND123456789PRETEND6789 -r 0456aa5842946PRETEND4f37c97a0e5"
42
+ ```
43
+
44
+ Likely, you will want to store these codes in your environment variables and then use them like this:
45
+
46
+ ```
47
+ (windows)
48
+ npx docu-notion -n %MY_NOTION_TOKEN% -r %MY_NOTION_DOCS_ROOT_PAGE_ID%
49
+ ```
50
+
51
+ ```
52
+ (linux / mac)
53
+ npx docu-notion -n $MY_NOTION_TOKEN -r $MY_NOTION_DOCS_ROOT_PAGE_ID
54
+ ```
55
+
56
+ NOTE: In the above, we are using `npx` to use the latest `docu-notion`. A more conservative approach would be to `npm i cross-var docu-notion` and then create a script in your package.json like this:
57
+
58
+ ```
59
+ "scripts": {
60
+ "pull": "cross-var docu-notion -n %DOCU_NOTION_INTEGRATION_TOKEN% -r %DOCU_NOTION_ROOT_PAGE%"
61
+ }
62
+ ```
63
+
64
+ and then run that with `npm run pull`.
65
+
66
+ ## 7. Commit
67
+
68
+ Most projects should probably commit the current markdown and image files each time you run docu-notion.
69
+
70
+ Note that if you choose not to commit, the workflow feature (see below) won't work for you. Imagine the case where a document that previously had a `Status` property of `Publish` now has a different status. You probably want to keep publishing the old version until the new one is ready. But if you don't commit files, your CI system (e.g. Github Actions) won't have the old version around, so it will disappear from your site.
71
+
72
+ # Using a Notion database for workflow
73
+
74
+ One of the big attractions of Notion for large documentation projects is that you can treat your pages as database items. The advantage of this is that they can then have metadata properties that fit your workflow. For example, we use a simple kanban board view to see where each page is in our workflow:
75
+
76
+ ![image](https://user-images.githubusercontent.com/8448/168929745-e6529375-bb1e-47e9-b8a6-7a1467c8900f.png)
77
+
78
+ `docu-notion` supports this by letting you link to database pages from your outline.
79
+
80
+ ![image](https://user-images.githubusercontent.com/8448/168929668-f83d7c86-75d2-48e9-940c-84c5268a2854.png)
81
+
82
+ # Page properties
83
+
84
+ ![image](https://user-images.githubusercontent.com/8448/197016100-ab016111-2fa1-420a-a884-05318783096e.png)
85
+
86
+ > **Note**
87
+ > For some reason Notion only allows properties on pages that are part of a database. So if you create pages directly in the Outline, you won't be able to fill in any of these properties, other than the page title.
88
+
89
+ ## Slugs
90
+
91
+ By default, pages will be given a slug based on the Notion id. For a human-readable URL, add a notion property named `Slug` to your database pages and enter a value in there that will work well in a URL. That is, no spaces, ?, #, /, etc.
92
+
93
+ ## Known Limitations
94
+
95
+ docu-notion is not doing anything smart with regards to previously Published but now not Published documents. All it does is ignore every Notion document that doesn't have `status == Publish`. So if the old version of the document is still in your file tree when your static site generator (e.g. Docusaurus) runs, then it will appear on your website. If it isn't there, it won't. If you rename directories or move the document, docu-notion will not realize this and will delete the previously published markdown file.
96
+
97
+ # Text Localization
98
+
99
+ Localize your files in Crowdin (or whatever) based on the markdown files, not in Notion. For how to do this with Docusaurus, see [Docusaurus i18n](https://docusaurus.io/docs/i18n/crowdin).
100
+
101
+ # Screenshot Localization
102
+
103
+ The only way we know of to provide localization of image in the current Docusaurus (2.0) is to place the images in the same directory as the markdown, and use relative paths for images. Most projects probably won't localize _every_ image, so we also need a way to "fall back" to the original screenshot when the localized one is missing. `docu-notion` facilitates this. If no localized version of an image is available, `docu-notion` places a copy of the original image into the correct location.
104
+
105
+ So how do you provide these localized screenshot files? Crowdin can handle localizing assets, and in the future we may support that. For now, we currently support a different approach. If you place for example `fr https:\\imgur.com\1234.png` in the caption of a screenshot in Notion, `docu-notion` will fetch that image and save it in the right place to be found when in French mode. Getting URLs to screenshots is easy with screenshot utilities such as [Greenshot](https://getgreenshot.org/) that support uploading to imgur. Note that `docu-notion` stores a copy of all images in your source tree, so you wouldn't lose the images if imgur were to go away.
106
+
107
+ NOTE: that as far as I can tell, when you run `docusaurus start` docusaurus 2.0 offers the language picker but it doesn't actually work. So to test out the localized version, do `docusaurus build` followed by `docusaurus serve`.
108
+
109
+ NOTE: if you just localize an image, it will not get picked up. You also must localize the page that uses the image. Otherwise, Docusaurus will use the English document and when that asks for `./the-image-path`, it will find the image there in the English section, not your other language section.
110
+
111
+ # Automated builds with Github Actions
112
+
113
+ Here is a working Github Action script to copy and customize: https://github.com/BloomBooks/bloom-docs/blob/master/.github/workflows/release.yml
114
+
115
+ # Command line
116
+
117
+ Usage: docu-notion -n <token> -r <root> [options]
118
+
119
+ Options:
120
+
121
+ | flag | required? | description |
122
+ | ------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
123
+ | -n, --notion-token <string> | required | notion api token, which looks like `secret_3bc1b50XFYb15123RHF243x43450XFY33250XFYa343` |
124
+ | -r, --root-page <string> | required | The 31 character ID of the page which is the root of your docs page in notion. The code will look like `9120ec9960244ead80fa2ef4bc1bba25`. This page must have a child page named 'Outline' |
125
+ | -m, --markdown-output-path <string> | | Root of the hierarchy for md files. WARNING: node-pull-mdx will delete files from this directory. Note also that if it finds localized images, it will create an i18n/ directory as a sibling. (default: "./docs") |
126
+ | -t, --status-tag <string> | | Database pages without a Notion page property 'status' matching this will be ignored. Use '\*' to ignore status altogether. (default: `Publish`) |
127
+ | --locales <codes> | | Comma-separated list of iso 639-2 codes, the same list as in docusaurus.config.js, minus the primary (i.e. 'en'). This is needed for image localization. (default: []) |
128
+ | -l, --log-level <level> | | Log level (choices: `info`, `verbose`, `debug`) |
129
+ | -i, --img-output-path <string> | | Path to directory where images will be stored. If this is not included, images will be placed in the same directory as the document that uses them, which then allows for localization of screenshots. |
130
+ | -p, --img-prefix-in-markdown <string> | | When referencing an image from markdown, prefix with this path instead of the full img-output-path. Should be used only in conjunction with --img-output-path. |
131
+ | -h, --help | | display help for command |
132
+
133
+ # Plugins
134
+
135
+ If your project needs some processing that docu-notion doesn't already provide, you can provide a plugin that does it. See the [plugin readme](src/plugins/README.md).
@@ -0,0 +1,6 @@
1
+ import { LayoutStrategy } from "./LayoutStrategy";
2
+ import { NotionPage } from "./NotionPage";
3
+ export declare class FlatGuidLayoutStrategy extends LayoutStrategy {
4
+ newLevel(rootDir: string, order: number, context: string, _levelLabel: string): string;
5
+ getPathForPage(page: NotionPage, extensionWithDot: string): string;
6
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FlatGuidLayoutStrategy = void 0;
4
+ const LayoutStrategy_1 = require("./LayoutStrategy");
5
+ // This strategy creates a flat list of files that have notion-id for file names.
6
+ // Pros: the urls will never change so long as the notion pages are not delete and re-recreated.
7
+ // Cons: the names are human readable, so:
8
+ // * troubleshooting is more difficult
9
+ // * is less "future" proof, in the sense that if you someday take these files and move them
10
+ // * to a new system, maybe you will wish the files had names.
11
+ // TODO: for this to be viable, we'd also have to emit info on how to build the sidebar, because
12
+ // the directory/file structure itself is no longer representative of the outline we want.
13
+ class FlatGuidLayoutStrategy extends LayoutStrategy_1.LayoutStrategy {
14
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
15
+ newLevel(rootDir, order, context, _levelLabel) {
16
+ // In this strategy, we ignore context and don't create any directories to match the levels.
17
+ // Just return the following for the benefit of logging.
18
+ return context + "/" + _levelLabel;
19
+ }
20
+ getPathForPage(page, extensionWithDot) {
21
+ // In this strategy, we don't care about the location or the title
22
+ return this.rootDirectory + "/" + page.pageId + extensionWithDot;
23
+ }
24
+ }
25
+ exports.FlatGuidLayoutStrategy = FlatGuidLayoutStrategy;
@@ -0,0 +1,7 @@
1
+ import { LayoutStrategy } from "./LayoutStrategy";
2
+ import { NotionPage } from "./NotionPage";
3
+ export declare class HierarchicalNamedLayoutStrategy extends LayoutStrategy {
4
+ newLevel(dirRoot: string, order: number, context: string, levelLabel: string): string;
5
+ getPathForPage(page: NotionPage, extensionWithDot: string): string;
6
+ private addCategoryMetadata;
7
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.HierarchicalNamedLayoutStrategy = void 0;
30
+ const fs = __importStar(require("fs-extra"));
31
+ const sanitize_filename_1 = __importDefault(require("sanitize-filename"));
32
+ const LayoutStrategy_1 = require("./LayoutStrategy");
33
+ // This strategy gives us a file tree that mirrors that of notion.
34
+ // Each level in the outline becomes a directory, and each file bears the name of the Notion document.
35
+ // As long as you use slugs, the urls is still just something like https://site/slug
36
+ class HierarchicalNamedLayoutStrategy extends LayoutStrategy_1.LayoutStrategy {
37
+ newLevel(dirRoot, order, context, levelLabel) {
38
+ const path = context + "/" + (0, sanitize_filename_1.default)(levelLabel).replaceAll(" ", "-");
39
+ //console.log("Creating level " + path);
40
+ const newPath = dirRoot + "/" + path;
41
+ fs.mkdirSync(newPath, { recursive: true });
42
+ this.addCategoryMetadata(newPath, order, levelLabel);
43
+ return path;
44
+ }
45
+ getPathForPage(page, extensionWithDot) {
46
+ const sanitizedName = (0, sanitize_filename_1.default)(page.nameForFile())
47
+ .replaceAll("//", "/")
48
+ .replaceAll("%20", "-")
49
+ .replaceAll(" ", "-")
50
+ // crowdin complains about some characters in file names. I haven't found
51
+ // the actual list, so these are from memory.
52
+ .replaceAll('"', "")
53
+ .replaceAll("“", "")
54
+ .replaceAll("”", "")
55
+ .replaceAll("'", "")
56
+ .replaceAll("?", "-");
57
+ const context = ("/" + page.layoutContext + "/").replaceAll("//", "/");
58
+ const path = this.rootDirectory + context + sanitizedName + extensionWithDot;
59
+ return path;
60
+ }
61
+ //{
62
+ // "position": 2.5,
63
+ // "label": "Tutorial",
64
+ // "collapsible": true,
65
+ // "collapsed": false,
66
+ // "className": "red",
67
+ // "link": {
68
+ // "type": "generated-index",
69
+ // "title": "Tutorial overview"
70
+ // },
71
+ // "customProps": {
72
+ // "description": "This description can be used in the swizzled DocCard"
73
+ // }
74
+ // }
75
+ addCategoryMetadata(dir, order, label) {
76
+ const data = `{"position":${order}, "label":"${label}"}`;
77
+ fs.writeFileSync(dir + "/_category_.json", data);
78
+ }
79
+ }
80
+ exports.HierarchicalNamedLayoutStrategy = HierarchicalNamedLayoutStrategy;
@@ -0,0 +1,12 @@
1
+ import { NotionPage } from "./NotionPage";
2
+ export declare abstract class LayoutStrategy {
3
+ protected rootDirectory: string;
4
+ protected existingPagesNotSeenYetInPull: string[];
5
+ setRootDirectoryForMarkdown(markdownOutputPath: string): void;
6
+ cleanupOldFiles(): Promise<void>;
7
+ abstract newLevel(rootDir: string, order: number, context: string, levelLabel: string): string;
8
+ abstract getPathForPage(page: NotionPage, extensionWithDot: string): string;
9
+ getLinkPathForPage(page: NotionPage): string;
10
+ pageWasSeen(page: NotionPage): void;
11
+ protected getListOfExistingFiles(dir: string): string[];
12
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ exports.LayoutStrategy = void 0;
36
+ const fs = __importStar(require("fs-extra"));
37
+ const log_1 = require("./log");
38
+ // Here a fuller name would be File Tree Layout Strategy. That is,
39
+ // as we walk the Notion outline and create files, where do we create them, what do we name them, etc.
40
+ class LayoutStrategy {
41
+ constructor() {
42
+ this.rootDirectory = "";
43
+ this.existingPagesNotSeenYetInPull = [];
44
+ }
45
+ setRootDirectoryForMarkdown(markdownOutputPath) {
46
+ this.rootDirectory = markdownOutputPath;
47
+ this.existingPagesNotSeenYetInPull =
48
+ this.getListOfExistingFiles(markdownOutputPath);
49
+ }
50
+ cleanupOldFiles() {
51
+ return __awaiter(this, void 0, void 0, function* () {
52
+ // Remove any pre-existing files that aren't around anymore; this indicates that they were removed or renamed in Notion.
53
+ for (const p of this.existingPagesNotSeenYetInPull) {
54
+ (0, log_1.verbose)(`Removing old doc: ${p}`);
55
+ yield fs.rm(p);
56
+ }
57
+ });
58
+ }
59
+ getLinkPathForPage(page) {
60
+ // the url we return starts with a "/", meaning it is relative to the root of the markdown root (e.g. /docs root in Docusaurus)
61
+ return ("/" + page.slug).replaceAll("//", "/");
62
+ }
63
+ pageWasSeen(page) {
64
+ const path = this.getPathForPage(page, ".md");
65
+ this.existingPagesNotSeenYetInPull =
66
+ this.existingPagesNotSeenYetInPull.filter(p => p !== path);
67
+ }
68
+ getListOfExistingFiles(dir) {
69
+ return fs.readdirSync(dir).flatMap(item => {
70
+ const path = `${dir}/${item}`;
71
+ if (fs.statSync(path).isDirectory()) {
72
+ return this.getListOfExistingFiles(path);
73
+ }
74
+ if (path.endsWith(".md")) {
75
+ // we could just notice all files, and maybe that's better. But then we lose an debugging files like .json of the raw notion, on the second run.
76
+ return [path];
77
+ }
78
+ else
79
+ return [];
80
+ });
81
+ }
82
+ }
83
+ exports.LayoutStrategy = LayoutStrategy;
@@ -0,0 +1,2 @@
1
+ import { ImageSet } from "./images";
2
+ export declare function makeImagePersistencePlan(imageSet: ImageSet, imageOutputRootPath: string, imagePrefix: string): void;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.makeImagePersistencePlan = void 0;
27
+ const Path = __importStar(require("path"));
28
+ const log_1 = require("./log");
29
+ const process_1 = require("process");
30
+ function makeImagePersistencePlan(imageSet, imageOutputRootPath, imagePrefix) {
31
+ var _a;
32
+ if ((_a = imageSet.fileType) === null || _a === void 0 ? void 0 : _a.ext) {
33
+ // Since most images come from pasting screenshots, there isn't normally a filename. That's fine, we just make a hash of the url
34
+ // Images that are stored by notion come to us with a complex url that changes over time, so we pick out the UUID that doesn't change. Example:
35
+ // https://s3.us-west-2.amazonaws.com/secure.notion-static.com/d1058f46-4d2f-4292-8388-4ad393383439/Untitled.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20220516%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20220516T233630Z&X-Amz-Expires=3600&X-Amz-Signature=f215704094fcc884d37073b0b108cf6d1c9da9b7d57a898da38bc30c30b4c4b5&X-Amz-SignedHeaders=host&x-id=GetObject
36
+ let thingToHash = imageSet.primaryUrl;
37
+ const m = /.*secure\.notion-static\.com\/(.*)\//gm.exec(imageSet.primaryUrl);
38
+ if (m && m.length > 1) {
39
+ thingToHash = m[1];
40
+ }
41
+ const hash = hashOfString(thingToHash);
42
+ imageSet.outputFileName = `${hash}.${imageSet.fileType.ext}`;
43
+ imageSet.primaryFileOutputPath = Path.posix.join((imageOutputRootPath === null || imageOutputRootPath === void 0 ? void 0 : imageOutputRootPath.length) > 0
44
+ ? imageOutputRootPath
45
+ : imageSet.pathToParentDocument, imageSet.outputFileName);
46
+ if (imageOutputRootPath && imageSet.localizedUrls.length) {
47
+ (0, log_1.error)("imageOutputPath was declared, but one or more localizedUrls were found too. If you are going to localize screenshots, then you can't declare an imageOutputPath.");
48
+ (0, process_1.exit)(1);
49
+ }
50
+ imageSet.filePathToUseInMarkdown =
51
+ ((imagePrefix === null || imagePrefix === void 0 ? void 0 : imagePrefix.length) > 0 ? imagePrefix : ".") +
52
+ "/" +
53
+ imageSet.outputFileName;
54
+ }
55
+ else {
56
+ (0, log_1.error)(`Something wrong with the filetype extension on the blob we got from ${imageSet.primaryUrl}`);
57
+ (0, process_1.exit)(1);
58
+ }
59
+ }
60
+ exports.makeImagePersistencePlan = makeImagePersistencePlan;
61
+ function hashOfString(s) {
62
+ let hash = 0;
63
+ for (let i = 0; i < s.length; ++i)
64
+ hash = Math.imul(31, hash) + s.charCodeAt(i);
65
+ return Math.abs(hash);
66
+ }
@@ -0,0 +1 @@
1
+ export {};