@riebeckite/plugin-folder-pages 0.0.16 → 0.0.18

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/dist/index.js CHANGED
@@ -13,21 +13,22 @@ function collapseFolderEntryLocations(locations) {
13
13
  owners.set(location.permalink, location.slug);
14
14
  }
15
15
  }
16
- const byFolder = /* @__PURE__ */ new Map();
17
- for (const slug of locations.keys()) {
18
- const basename = lastSegment(slug);
16
+ const byFolderLanguage = /* @__PURE__ */ new Map();
17
+ for (const [slug, location] of locations) {
18
+ const basename = entryBaseSegment(slug, location.language);
19
19
  if (!isEntryBasename(basename)) continue;
20
- const folder = parentFolder(slug);
21
- const slugs = byFolder.get(folder);
20
+ const key = languageKey(parentFolder(slug), location.language);
21
+ const slugs = byFolderLanguage.get(key);
22
22
  if (slugs) slugs.push(slug);
23
- else byFolder.set(folder, [slug]);
23
+ else byFolderLanguage.set(key, [slug]);
24
24
  }
25
- for (const slugs of byFolder.values()) {
25
+ for (const slugs of byFolderLanguage.values()) {
26
26
  if (slugs.length !== 1) continue;
27
27
  const slug = slugs[0];
28
+ if (slug === void 0) continue;
28
29
  const location = locations.get(slug);
29
30
  if (!location) continue;
30
- const basename = lastSegment(slug);
31
+ const basename = entryBaseSegment(slug, location.language);
31
32
  if (!location.permalink.endsWith(`/${basename}`)) continue;
32
33
  const target = location.permalink.slice(
33
34
  0,
@@ -46,83 +47,111 @@ function collapseFolderEntryLocations(locations) {
46
47
  }
47
48
  }
48
49
  function buildFolderPages(manifest) {
49
- const pagesByFolder = /* @__PURE__ */ new Map();
50
- const subfoldersOf = /* @__PURE__ */ new Map();
51
- const allFolders = /* @__PURE__ */ new Set();
52
50
  const routableBySlug = /* @__PURE__ */ new Map();
53
51
  const routablePermalinks = /* @__PURE__ */ new Set();
54
52
  for (const entry of manifest.publicEntries) {
55
53
  routableBySlug.set(entry.slug, entry);
56
54
  routablePermalinks.add(stripTrailingSlash(entry.permalink));
57
55
  }
58
- const addSubfolder = (parent, child) => {
59
- const subs = subfoldersOf.get(parent);
60
- if (subs) subs.add(child);
61
- else subfoldersOf.set(parent, /* @__PURE__ */ new Set([child]));
62
- allFolders.add(parent);
63
- allFolders.add(child);
56
+ const allFolders = /* @__PURE__ */ new Set();
57
+ const folderLanguages = /* @__PURE__ */ new Map();
58
+ const pagesByFolderLanguage = /* @__PURE__ */ new Map();
59
+ const subfolders = /* @__PURE__ */ new Map();
60
+ const addLanguage = (folder, language) => {
61
+ const set = folderLanguages.get(folder);
62
+ if (set) set.add(language);
63
+ else folderLanguages.set(folder, /* @__PURE__ */ new Set([language]));
64
64
  };
65
- for (const entry of manifest.discoverableEntries) {
66
- const slug = entry.slug;
67
- const basename = lastSegment(slug);
68
- const parent = parentFolder(slug);
69
- allFolders.add(parent);
70
- if (!isEntryBasename(basename)) {
71
- const pages2 = pagesByFolder.get(parent);
72
- if (pages2) pages2.push(entry);
73
- else pagesByFolder.set(parent, [entry]);
65
+ const addPage = (folder, language, entry) => {
66
+ let byLanguage = pagesByFolderLanguage.get(folder);
67
+ if (!byLanguage) {
68
+ byLanguage = /* @__PURE__ */ new Map();
69
+ pagesByFolderLanguage.set(folder, byLanguage);
74
70
  }
75
- let child = parent;
71
+ const entries = byLanguage.get(language);
72
+ if (entries) entries.push(entry);
73
+ else byLanguage.set(language, [entry]);
74
+ };
75
+ const addSubfolder = (parent, language, child) => {
76
+ let byLanguage = subfolders.get(parent);
77
+ if (!byLanguage) {
78
+ byLanguage = /* @__PURE__ */ new Map();
79
+ subfolders.set(parent, byLanguage);
80
+ }
81
+ const set = byLanguage.get(language);
82
+ if (set) set.add(child);
83
+ else byLanguage.set(language, /* @__PURE__ */ new Set([child]));
84
+ };
85
+ for (const entry of manifest.discoverableEntries) {
86
+ const language = entry.publicLocation.language;
87
+ const folder = parentFolder(entry.slug);
88
+ allFolders.add(folder);
89
+ let child = folder;
76
90
  while (child !== "") {
91
+ addLanguage(child, language);
77
92
  const grandparent = parentFolder(child);
78
- addSubfolder(grandparent, child);
93
+ addSubfolder(grandparent, language, child);
79
94
  child = grandparent;
80
95
  }
96
+ if (isEntryBasename(entryBaseSegment(entry.slug, language))) continue;
97
+ if (folder !== "") addPage(folder, language, entry);
81
98
  }
82
- const hasRoutableOwner = (folder) => routableBySlug.has(`${folder}/README`) || routableBySlug.has(`${folder}/index`);
83
- const hasRoutablePage = (folder) => routableBySlug.has(folder);
84
- const sortedPages = (folder) => {
85
- const subfolders = subfoldersOf.get(folder);
86
- const pages2 = (pagesByFolder.get(folder) ?? []).filter(
87
- (entry) => !subfolders?.has(entry.slug)
88
- );
99
+ const owners = /* @__PURE__ */ new Map();
100
+ for (const entry of manifest.publicEntries) {
101
+ const language = entry.publicLocation.language;
102
+ const basename = entryBaseSegment(entry.slug, language);
103
+ if (!isEntryBasename(basename)) continue;
104
+ const folder = parentFolder(entry.slug);
105
+ if (folder === "") continue;
106
+ const key = languageKey(folder, language);
107
+ const existing = owners.get(key);
108
+ if (!existing || basename === "README") owners.set(key, entry);
109
+ }
110
+ const hasOwner = (folder, language) => owners.has(languageKey(folder, language));
111
+ const hasRoutablePage = (folder, language) => {
112
+ const entry = routableBySlug.get(folder);
113
+ return entry !== void 0 && entry.publicLocation.language === language;
114
+ };
115
+ const generatedPath = (folder, language) => resolveGeneratedFolderLocation(manifest, folder, language);
116
+ const subfoldersFor = (folder, language) => subfolders.get(folder)?.get(language) ?? /* @__PURE__ */ new Set();
117
+ const sortedPages = (folder, language) => {
118
+ const subs = subfoldersFor(folder, language);
119
+ const pages2 = (pagesByFolderLanguage.get(folder)?.get(language) ?? []).filter((entry) => !subs.has(entry.slug));
89
120
  pages2.sort(compareEntries);
90
121
  return pages2;
91
122
  };
92
123
  const navigableCache = /* @__PURE__ */ new Map();
93
- const isNavigable = (folder) => {
94
- const cached = navigableCache.get(folder);
124
+ const isNavigable = (folder, language) => {
125
+ const key = languageKey(folder, language);
126
+ const cached = navigableCache.get(key);
95
127
  if (cached !== void 0) return cached;
96
- navigableCache.set(folder, false);
97
- let result = sortedPages(folder).length > 0 || hasRoutableOwner(folder) || hasRoutablePage(folder);
128
+ navigableCache.set(key, false);
129
+ let result = sortedPages(folder, language).length > 0 || hasOwner(folder, language) || hasRoutablePage(folder, language);
98
130
  if (!result) {
99
- for (const sub of subfoldersOf.get(folder) ?? []) {
100
- if (isListed(sub)) {
131
+ for (const sub of subfoldersFor(folder, language)) {
132
+ if (isListed(sub, language)) {
101
133
  result = true;
102
134
  break;
103
135
  }
104
136
  }
105
137
  }
106
- navigableCache.set(folder, result);
138
+ navigableCache.set(key, result);
107
139
  return result;
108
140
  };
109
- const isListed = (folder) => hasRoutableOwner(folder) || hasRoutablePage(folder) || isNavigable(folder) && generatedPath(folder) !== null;
110
- const generatedPath = (folder) => resolveGeneratedFolderLocation(manifest, folder);
111
- const folderLink = (folder) => {
112
- if (hasRoutableOwner(folder)) {
113
- const owner = routableBySlug.get(`${folder}/README`) ?? routableBySlug.get(`${folder}/index`);
114
- if (owner) return { title: owner.title, permalink: owner.permalink };
115
- }
116
- const page = routableBySlug.get(folder);
141
+ const isListed = (folder, language) => hasOwner(folder, language) || hasRoutablePage(folder, language) || isNavigable(folder, language) && generatedPath(folder, language) !== null;
142
+ const folderLink = (folder, language) => {
143
+ const owner = owners.get(languageKey(folder, language));
144
+ if (owner) return { title: owner.title, permalink: owner.permalink };
145
+ const page = hasRoutablePage(folder, language) ? routableBySlug.get(folder) : void 0;
117
146
  if (page) return { title: page.title, permalink: page.permalink };
118
- const pathname = generatedPath(folder);
147
+ const pathname = generatedPath(folder, language);
119
148
  if (!pathname) return null;
120
149
  return { title: lastSegment(folder), permalink: pathname };
121
150
  };
122
- const sortedSubfolders = (folder) => {
123
- const subs = [...subfoldersOf.get(folder) ?? []].flatMap((sub) => {
124
- if (!isListed(sub)) return [];
125
- const link = folderLink(sub);
151
+ const sortedSubfolders = (folder, language) => {
152
+ const subs = [...subfoldersFor(folder, language)].flatMap((sub) => {
153
+ if (!isListed(sub, language)) return [];
154
+ const link = folderLink(sub, language);
126
155
  return link ? [{ folder: sub, link }] : [];
127
156
  });
128
157
  subs.sort((a, b) => compareLinks(a.link, b.link));
@@ -133,36 +162,59 @@ function buildFolderPages(manifest) {
133
162
  const pages = [];
134
163
  const takenPaths = /* @__PURE__ */ new Set();
135
164
  for (const folder of folders) {
136
- if (!isNavigable(folder)) continue;
137
- if (hasRoutableOwner(folder)) continue;
138
- if (hasRoutablePage(folder)) continue;
139
- const pathname = generatedPath(folder);
140
- if (!pathname) continue;
141
- if (pathname === "/") continue;
142
- if (routablePermalinks.has(stripTrailingSlash(pathname))) continue;
143
- if (takenPaths.has(pathname)) continue;
144
- takenPaths.add(pathname);
145
- const folders_ = sortedSubfolders(folder);
146
- pages.push({
147
- folder,
148
- pathname,
149
- title: lastSegment(folder),
150
- pages: sortedPages(folder).map(toLink),
151
- folders: folders_.map((sub) => sub.link),
152
- dependencies: [
153
- { type: "folder", folder },
154
- ...folders_.map((sub) => ({
155
- type: "folder",
156
- folder: sub.folder
157
- }))
158
- ]
159
- });
165
+ for (const language of sortedLanguages(folderLanguages.get(folder))) {
166
+ if (!isNavigable(folder, language)) continue;
167
+ if (hasOwner(folder, language)) continue;
168
+ if (hasRoutablePage(folder, language)) continue;
169
+ const pathname = generatedPath(folder, language);
170
+ if (!pathname) continue;
171
+ if (pathname === "/") continue;
172
+ if (routablePermalinks.has(stripTrailingSlash(pathname))) continue;
173
+ if (takenPaths.has(pathname)) continue;
174
+ takenPaths.add(pathname);
175
+ const folderLinks = sortedSubfolders(folder, language);
176
+ pages.push({
177
+ folder,
178
+ pathname,
179
+ language,
180
+ title: lastSegment(folder),
181
+ pages: sortedPages(folder, language).map(toLink),
182
+ folders: folderLinks.map((sub) => sub.link),
183
+ dependencies: [
184
+ { type: "folder", folder },
185
+ ...folderLinks.map((sub) => ({
186
+ type: "folder",
187
+ folder: sub.folder
188
+ }))
189
+ ]
190
+ });
191
+ }
160
192
  }
161
193
  pages.sort((a, b) => compareStrings(a.pathname, b.pathname));
162
194
  const byPath = /* @__PURE__ */ new Map();
163
195
  for (const page of pages) byPath.set(page.pathname, page);
164
196
  return { paths: pages.map((page) => page.pathname), byPath };
165
197
  }
198
+ function languageKey(folder, language) {
199
+ return `${folder}\0${language ?? ""}`;
200
+ }
201
+ function entryBaseSegment(slug, language) {
202
+ const segment = lastSegment(slug);
203
+ if (!language) return segment;
204
+ return segment.replace(
205
+ new RegExp(`[._-]${escapeRegExp(language)}$`, "i"),
206
+ ""
207
+ );
208
+ }
209
+ function sortedLanguages(languages) {
210
+ return [...languages ?? []].sort((a, b) => {
211
+ const left = a ?? "";
212
+ const right = b ?? "";
213
+ if (left < right) return -1;
214
+ if (left > right) return 1;
215
+ return 0;
216
+ });
217
+ }
166
218
  function isEntryBasename(basename) {
167
219
  return ENTRY_BASENAMES.includes(basename);
168
220
  }
@@ -181,6 +233,9 @@ function stripTrailingSlash(url) {
181
233
  if (url.length > 1 && url.endsWith("/")) return url.slice(0, -1);
182
234
  return url;
183
235
  }
236
+ function escapeRegExp(value) {
237
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
238
+ }
184
239
  function compareEntries(a, b) {
185
240
  return compareStrings(a.permalink, b.permalink) || compareStrings(a.title, b.title) || compareStrings(a.slug, b.slug);
186
241
  }
@@ -273,7 +328,11 @@ function folderPages(options = {}) {
273
328
  },
274
329
  onManifestCreated: ({ manifest }) => {
275
330
  for (const page of modelFor(manifest).byPath.values()) {
276
- manifest.folderLocations.set(page.folder, { pathname: page.pathname });
331
+ manifest.folderLocations.set(page.pathname, {
332
+ pathname: page.pathname,
333
+ folder: page.folder,
334
+ ...page.language === void 0 ? {} : { language: page.language }
335
+ });
277
336
  }
278
337
  },
279
338
  pageTypes: [
@@ -292,7 +351,8 @@ function folderPages(options = {}) {
292
351
  type: "folder-page",
293
352
  pathname: page.pathname,
294
353
  title: rendered.title,
295
- body: rendered.html
354
+ body: rendered.html,
355
+ language: page.language
296
356
  };
297
357
  }
298
358
  }
@@ -16,6 +16,7 @@ export type FolderPageLink = {
16
16
  export type FolderPage = {
17
17
  folder: string;
18
18
  pathname: string;
19
+ language?: string;
19
20
  title: string;
20
21
  pages: FolderPageLink[];
21
22
  folders: FolderPageLink[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/plugin-folder-pages",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -14,12 +14,12 @@
14
14
  }
15
15
  },
16
16
  "dependencies": {
17
- "@riebeckite/core": "0.0.16"
17
+ "@riebeckite/core": "0.0.18"
18
18
  },
19
19
  "devDependencies": {
20
20
  "tsx": "^4.22.4",
21
- "@riebeckite/plugin-l10n": "0.0.16",
22
- "@riebeckite/plugin-taxonomy": "0.0.16"
21
+ "@riebeckite/plugin-taxonomy": "0.0.18",
22
+ "@riebeckite/plugin-l10n": "0.0.18"
23
23
  },
24
24
  "publishConfig": {
25
25
  "access": "public"