scripts/stdlib/generate-redirects.js (127 lines of code) (raw):

const { promises: { readdir, readFile } } = require('fs'); const { join } = require('path'); const { OLD_PATH_PREFIX, writeRedirects, writeUnmatched, exists } = require('./utils'); const Redirects = require('./redirect-collector'); const LinksProcessor = require('./links-processor'); /** * In general the mapping is next: * stdlib /api/latest/jvm/stdlib/ - new /kotlin-stdlib/index.html * new /kotlin-reflect/index.html * test /api/latest/kotlin.test/ - new /kotlin-test/index.html */ const redirects = new Redirects(); const folders = new Set(); makeStdlibRedirects() .then(async () => { for (const [from, to] of redirects.redirects.entries()) { const direction = to.replace(/^\/api\//, 'api/'); if (!redirects.matched.get(direction)) { const parts = direction.split('/'); const name = parts.pop(); const dir = parts.join('/'); await addFile(dir, name); } const value = redirects.matched.get(direction); if (value) { redirects.add(from, value); redirects.redirects.delete(from); } } if (redirects.redirects.size > 0) console.log(`unresolved ${redirects.redirects.size} redirects...`); return [...redirects.matched.entries()]; }) .then(urls => Promise.all([ writeRedirects('redirects/stdlib-redirects.yml', urls), writeUnmatched('not-found.json', [...redirects.unmatched]) ])); async function makeStdlibRedirects() { await readFiles(OLD_PATH_PREFIX, ''); for (const folder of folders) { await readFiles(folder); } } async function readFiles(currentPath) { const dirents = await readdir(currentPath, { withFileTypes: true }); await Promise.all( dirents.map(async item => { const itemPath = `${currentPath}/${item.name}`; if (item.isDirectory()) folders.add(itemPath); else if (item.isFile() && item.name.endsWith('.html')) await addFile(currentPath, item.name); else console.log(`${item.name} is incompatible file for redirect: ${itemPath}.`); }) ); } const MANUAL_LINKS = { // > I think it's the right thing to hide HIDDEN deprecated api from the documentation, // > so these pages can be redirected just to their containing package in the new docs. 'api/latest/kotlin.test/kotlin.test/assert-true-no-inline.html': 'api/core/kotlin-test/kotlin.test/index.html', 'api/latest/kotlin.test/kotlin.test/assert-false-no-inline.html': 'api/core/kotlin-test/kotlin.test/index.html', 'api/latest/kotlin.test/kotlin.test/expect-no-inline.html': 'api/core/kotlin-test/kotlin.test/index.html', 'api/latest/kotlin.test/kotlin.test/assert-not-null-no-inline.html': 'api/core/kotlin-test/kotlin.test/index.html', 'api/latest/jvm/stdlib/kotlin.collections/-abstract-mutable-collection/to-j-s-o-n.html': 'api/core/kotlin-stdlib/kotlin.collections/-abstract-mutable-collection/index.html' }; function getTargetPath(path, name) { return MANUAL_LINKS[path + '/' + name] || ( new LinksProcessor(path, name) .replaceAllTypes() .replaceKotlinJvmOptionals() .replaceKotlinReflect() .dropSomeKotlinPrefixes() // .replaceKotlinPackages() .dropEmptyPackages() .replaceRootPrefixes() .value ); } async function addFile(currentPath, name) { const oldPath = join(currentPath, name); const expectedPath = getTargetPath(currentPath, name); const expectedModulePath = expectedPath.split('/').slice(0, -1).join('/'); if (await exists(expectedPath)) { redirects.add(oldPath, expectedPath); return; } /** * Init file was removed */ if (name === '-init-.html') { const constructorName = expectedModulePath.split('/').pop(); const constructorPage = `${expectedModulePath}/${constructorName}.html`; if (await exists(constructorPage)) { redirects.add(oldPath, `${expectedModulePath}/${constructorName}.html`); return; } const indexPath = `${expectedModulePath}/index.html`; if (await exists(indexPath)) { redirects.add(oldPath, `${expectedModulePath}/index.html`); return; } } const companionPath = `${expectedModulePath}/-companion/${name}`; if (await exists(companionPath)) { redirects.add(oldPath, companionPath); return; } const insideDefaultPath = `${expectedModulePath}/-default/${name}`; if (await exists(insideDefaultPath)) { redirects.add(oldPath, insideDefaultPath); return; } const directoryName = `${expectedModulePath}/${name.replace(/\.html$/, '')}`; const nowDirectoryName = `${directoryName}/index.html`; if (await exists(nowDirectoryName)) { redirects.add(oldPath, nowDirectoryName); return; } const typeAliasFile = `${directoryName}-of/index.html`; if (await exists(typeAliasFile)) { console.log('hasTypeForTypeAlias'); return redirects.add(oldPath, typeAliasFile); } const text = await readFile(oldPath, { encoding: 'utf-8' }); const match = text.match(/<meta http-equiv="refresh" content="0; url=([^"]+)"\/>/); if (match && match[1]) { let to = match[1]; if (to.endsWith('/')) to += 'index.html'; redirects.addRedirect(oldPath, to); return; } redirects.addUnmatched(oldPath); }