Skip to content

Commit fbb55cf

Browse files
committed
wip specify locales and write fallback imgs when localized are absent
1 parent b88a02a commit fbb55cf

File tree

6 files changed

+62
-23
lines changed

6 files changed

+62
-23
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"editor.defaultFormatter": "esbenp.prettier-vscode"
55
},
66
"cSpell.words": [
7-
"callouts"
7+
"callouts",
8+
"unlocalized"
89
],
910
"workbench.colorCustomizations": {
1011
"statusBar.background": "#d649ca",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"notion-download": "node dist/index.js",
1616
"cmdhelp": "ts-node --compiler-options \"{\\\"module\\\": \\\"commonjs\\\"}\" src/index.ts",
1717
"// test out with my sample notion db": "",
18-
"sample": "cross-var ts-node --compiler-options \"{\\\"module\\\": \\\"commonjs\\\"}\" src/index.ts -n %NOTION_PULL_INTEGRATION_TOKEN% -r %NOTION_PULL_ROOT_PAGE% -m ./sample --log-level verbose",
18+
"sample": "cross-var ts-node --compiler-options \"{\\\"module\\\": \\\"commonjs\\\"}\" src/index.ts -n %NOTION_PULL_INTEGRATION_TOKEN% -r %NOTION_PULL_ROOT_PAGE% -m ./sample --locales en,es,fr,de --log-level verbose",
1919
"sample-with-paths": "cross-var ts-node --compiler-options \"{\\\"module\\\": \\\"commonjs\\\"}\" src/index.ts -n %NOTION_PULL_INTEGRATION_TOKEN% -r %NOTION_PULL_ROOT_PAGE% -m ./sample --img-output-path ./sample_img"
2020
},
2121
"repository": {

src/NotionImage.ts

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,19 @@ import { logDebug, verbose, info } from "./log";
88
let existingImagesNotSeenYetInPull: string[] = [];
99
let imageOutputPath = ""; // default to putting in the same directory as the document referring to it.
1010
let imagePrefix = ""; // default to "./"
11+
let locales: string[];
1112

12-
// we parse a notion image and its caption into what we need, which includes any urls to localized versions of the image that may be embedded in the caption
13+
// we parse a notion image and its caption into what we need, which includes any urls to localized versions
14+
// of the image that may be embedded in the caption.
1315
export type ImageSet = {
1416
// We get these from parseImageBlock():
1517
primaryUrl: string;
18+
// caption may contain a caption and/or URLs to localized versions
1619
caption?: string;
20+
// We use entries in localizedUrls whether or not we have a url, because if we don't have
21+
// a localized image, we then need to copy the primary image in, instead, to
22+
// get image fallback. In that case, the placeholder at least tells us what languages
23+
// are being supported.
1724
localizedUrls: Array<{ iso632Code: string; url: string }>;
1825

1926
// then we fill this in from processImageBlock():
@@ -32,12 +39,14 @@ export type ImageSet = {
3239

3340
export async function initImageHandling(
3441
prefix: string,
35-
outputPath: string
42+
outputPath: string,
43+
incomingLocales: string[]
3644
): Promise<void> {
3745
// If they gave us a trailing slash, remove it because we add it back later.
3846
// Note that it's up to the caller to have a *leading* slash or not.
3947
imagePrefix = prefix.replace(/\/$/, "");
4048
imageOutputPath = outputPath;
49+
locales = incomingLocales;
4150

4251
// Currently we don't delete the image directory, because if an image
4352
// changes, it gets a new id. This way can then prevent downloading
@@ -60,42 +69,52 @@ async function saveImage(imageSet: ImageSet): Promise<void> {
6069

6170
let foundLocalizedImage = false;
6271

63-
// if there are localized images, save them too, using the same
64-
// name as the primary but with their language code attached
6572
for (const localizedImage of imageSet.localizedUrls) {
66-
verbose(`Retrieving ${localizedImage.iso632Code} version...`);
67-
const response = await fetch(localizedImage.url);
68-
const arrayBuffer = await response.arrayBuffer();
69-
const buffer = Buffer.from(arrayBuffer);
73+
let buffer = imageSet.primaryBuffer!;
74+
// if we have a urls for the localized screenshot, download it
75+
if (localizedImage?.url.length > 0) {
76+
verbose(`Retrieving ${localizedImage.iso632Code} version...`);
77+
const response = await fetch(localizedImage.url);
78+
const arrayBuffer = await response.arrayBuffer();
79+
buffer = Buffer.from(arrayBuffer);
80+
} else {
81+
verbose(
82+
`No localized image specified for ${localizedImage.iso632Code}, will use primary image.`
83+
);
84+
// otherwise, we're going to fall back to outputting the primary image here
85+
}
7086
const directory = `./i18n/${
7187
localizedImage.iso632Code
7288
}/docusaurus-plugin-content-docs/current/${imageSet.relativePathToParentDocument!}`;
7389
if (!foundLocalizedImage) {
7490
foundLocalizedImage = true;
75-
info(
76-
"*** found at least one localized image, so /i18n directory will be created and filled with localized image files."
77-
);
7891
}
7992
writeImageIfNew(directory + "/" + imageSet.outputFileName!, buffer);
8093
}
8194
}
8295

8396
function writeImageIfNew(path: string, buffer: Buffer) {
8497
imageWasSeen(path);
85-
if (!fs.pathExistsSync(path)) {
98+
99+
// Note: it's tempting to not spend time writing this out if we already have
100+
// it from a previous run. But we don't really know it's the same. A) it
101+
// could just have the same name, B) it could have been previously
102+
// unlocalized and thus filled with a copy of the primary language image
103+
// while and now is localized.
104+
if (fs.pathExistsSync(path)) {
105+
verbose("Replacing image " + path);
106+
} else {
86107
verbose("Adding image " + path);
87108
fs.mkdirsSync(Path.dirname(path));
88-
fs.createWriteStream(path).write(buffer); // async but we're not waiting
89-
} else {
90-
verbose(`image already filled: ${path}`);
91109
}
110+
fs.createWriteStream(path).write(buffer); // async but we're not waiting
92111
}
93112

94113
export function parseImageBlock(b: any): ImageSet {
95114
const imageSet: ImageSet = {
96115
primaryUrl: "",
97116
caption: "",
98-
localizedUrls: [],
117+
localizedUrls: locales.map(l => ({ iso632Code: l, url: "" })),
99118
};
100119

101120
if ("file" in b.image) {

src/index.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,23 @@ program
1313
.requiredOption("-n, --notion-token <string>", "notion api token")
1414
.requiredOption(
1515
"-r, --root-page <string>",
16-
"the 31 character ID of the page which is the root of your notion docs"
16+
"The 31 character ID of the page which is the root of your notion docs"
1717
)
1818
.requiredOption(
1919
"-m, --markdown-output-path <string>",
20-
"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.",
20+
"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.",
2121
"./docs"
2222
)
2323
.option(
2424
"-t, --status-tag <string>",
2525
"Database pages without a Notion page property 'status' matching this will be ignored. Use '*' to ignore status altogether.",
2626
"Publish"
2727
)
28+
.option(
29+
"--locales <codes>",
30+
"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.",
31+
parseLocales
32+
)
2833
.addOption(
2934
new Option("-l, --log-level <level>", "Log level").choices([
3035
"info",
@@ -34,15 +39,15 @@ program
3439
)
3540
.option(
3641
"-i, --img-output-path <string>",
37-
"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."
42+
"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."
3843
)
3944
// .option(
4045
// "-l, --internal-link-prefix <string>",
4146
// "when converting a link from one page to another, prefix the with this path instead of the default, which is rooted at the markdown-output-path."
4247
// )
4348
.option(
4449
"-p, --img-prefix-in-markdown <string>",
45-
"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."
50+
"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."
4651
);
4752

4853
program.showHelpAfterError();
@@ -53,3 +58,7 @@ setLogLevel(program.opts().logLevel);
5358
void notionPull(program.opts() as Options).then(() =>
5459
console.log("notion-pull-mdx Finished.")
5560
);
61+
62+
function parseLocales(value: string): string[] {
63+
return value.split(",").map(l => l.trim().toLowerCase());
64+
}

src/makeImagePersistencePlan.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,11 @@ test("primary file with defaults for image output path and prefix", () => {
3131
);
3232
expect(imageSet.filePathToUseInMarkdown).toBe("./463556435.png");
3333
});
34+
35+
// In order to make image fallback work with other languages, we have to have
36+
// a file for each image, in each Docusaurus language directory. This is true
37+
// whether we have a localized version of the image or not.
38+
// The imageSet is initially populated with placeholders for each language.
39+
// This test ensures that these placeholders are replaced with actual urls
40+
// when localized versions of the image are listed.
41+
// TODO write this test

src/pull.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { error, info, verbose, warning } from "./log";
2121
export type Options = {
2222
notionToken: string;
2323
rootPage: string;
24+
locales: string[];
2425
markdownOutputPath: string;
2526
imgOutputPath: string;
2627
imgPrefixInMarkdown: string;
@@ -47,7 +48,8 @@ export async function notionPull(incomingOptions: Options): Promise<void> {
4748
verbose(JSON.stringify(optionsForLogging, null, 2));
4849
await initImageHandling(
4950
options.imgPrefixInMarkdown || options.imgOutputPath || "",
50-
options.imgOutputPath || ""
51+
options.imgOutputPath || "",
52+
options.locales
5153
);
5254

5355
const notionClient = initNotionClient(options.notionToken);

0 commit comments

Comments
 (0)