@riebeckite/plugin-folder-pages 0.0.17 → 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.
Files changed (2) hide show
  1. package/dist/index.js +139 -92
  2. package/package.json +4 -4
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,94 +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 folderLanguage = (folder) => {
112
- let language;
113
- for (const entry of manifest.discoverableEntries) {
114
- if (!entry.slug.startsWith(`${folder}/`)) continue;
115
- const value = entry.publicLocation.language;
116
- if (value === void 0) return void 0;
117
- if (language === void 0) language = value;
118
- else if (language !== value) return void 0;
119
- }
120
- return language;
121
- };
122
- const folderLink = (folder) => {
123
- if (hasRoutableOwner(folder)) {
124
- const owner = routableBySlug.get(`${folder}/README`) ?? routableBySlug.get(`${folder}/index`);
125
- if (owner) return { title: owner.title, permalink: owner.permalink };
126
- }
127
- 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;
128
146
  if (page) return { title: page.title, permalink: page.permalink };
129
- const pathname = generatedPath(folder);
147
+ const pathname = generatedPath(folder, language);
130
148
  if (!pathname) return null;
131
149
  return { title: lastSegment(folder), permalink: pathname };
132
150
  };
133
- const sortedSubfolders = (folder) => {
134
- const subs = [...subfoldersOf.get(folder) ?? []].flatMap((sub) => {
135
- if (!isListed(sub)) return [];
136
- 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);
137
155
  return link ? [{ folder: sub, link }] : [];
138
156
  });
139
157
  subs.sort((a, b) => compareLinks(a.link, b.link));
@@ -144,37 +162,59 @@ function buildFolderPages(manifest) {
144
162
  const pages = [];
145
163
  const takenPaths = /* @__PURE__ */ new Set();
146
164
  for (const folder of folders) {
147
- if (!isNavigable(folder)) continue;
148
- if (hasRoutableOwner(folder)) continue;
149
- if (hasRoutablePage(folder)) continue;
150
- const pathname = generatedPath(folder);
151
- if (!pathname) continue;
152
- if (pathname === "/") continue;
153
- if (routablePermalinks.has(stripTrailingSlash(pathname))) continue;
154
- if (takenPaths.has(pathname)) continue;
155
- takenPaths.add(pathname);
156
- const folders_ = sortedSubfolders(folder);
157
- pages.push({
158
- folder,
159
- pathname,
160
- language: folderLanguage(folder),
161
- title: lastSegment(folder),
162
- pages: sortedPages(folder).map(toLink),
163
- folders: folders_.map((sub) => sub.link),
164
- dependencies: [
165
- { type: "folder", folder },
166
- ...folders_.map((sub) => ({
167
- type: "folder",
168
- folder: sub.folder
169
- }))
170
- ]
171
- });
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
+ }
172
192
  }
173
193
  pages.sort((a, b) => compareStrings(a.pathname, b.pathname));
174
194
  const byPath = /* @__PURE__ */ new Map();
175
195
  for (const page of pages) byPath.set(page.pathname, page);
176
196
  return { paths: pages.map((page) => page.pathname), byPath };
177
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
+ }
178
218
  function isEntryBasename(basename) {
179
219
  return ENTRY_BASENAMES.includes(basename);
180
220
  }
@@ -193,6 +233,9 @@ function stripTrailingSlash(url) {
193
233
  if (url.length > 1 && url.endsWith("/")) return url.slice(0, -1);
194
234
  return url;
195
235
  }
236
+ function escapeRegExp(value) {
237
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
238
+ }
196
239
  function compareEntries(a, b) {
197
240
  return compareStrings(a.permalink, b.permalink) || compareStrings(a.title, b.title) || compareStrings(a.slug, b.slug);
198
241
  }
@@ -285,7 +328,11 @@ function folderPages(options = {}) {
285
328
  },
286
329
  onManifestCreated: ({ manifest }) => {
287
330
  for (const page of modelFor(manifest).byPath.values()) {
288
- 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
+ });
289
336
  }
290
337
  },
291
338
  pageTypes: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/plugin-folder-pages",
3
- "version": "0.0.17",
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.17"
17
+ "@riebeckite/core": "0.0.18"
18
18
  },
19
19
  "devDependencies": {
20
20
  "tsx": "^4.22.4",
21
- "@riebeckite/plugin-l10n": "0.0.17",
22
- "@riebeckite/plugin-taxonomy": "0.0.17"
21
+ "@riebeckite/plugin-taxonomy": "0.0.18",
22
+ "@riebeckite/plugin-l10n": "0.0.18"
23
23
  },
24
24
  "publishConfig": {
25
25
  "access": "public"