Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,18 @@ pdf/kotlin-reference.pdf
pdf/tmp.html
*.pyc
google-credentials.json
./package-lock.json
package-lock.json
.env
pages/api/
external/
assets/externals
pages/docs/tutorials/kotlin-for-py/
_teamcity
dist
/dist
libs
generated
.next
.teamcity/*.iml

/scripts/doindex/package-lock.json
/search-report*
/reports
/reports*
/data/page_views_map.json
8 changes: 2 additions & 6 deletions .teamcity/builds/TemplateSearchIndex.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import jetbrains.buildServer.configs.kotlin.buildSteps.ScriptBuildStep
import jetbrains.buildServer.configs.kotlin.triggers.schedule
import vcsRoots.KotlinLangOrg

const val SCRIPT_PATH = "scripts/doindex";
const val SCRIPT_PATH = "scripts/dist";

fun scriptDistAnalyze(block: ScriptBuildStep.() -> Unit) = ScriptBuildStep {
id = "script-dist-analyze"
Expand Down Expand Up @@ -42,11 +42,7 @@ abstract class TemplateSearchIndex(init: BuildType.() -> Unit) : BuildType({
}

vcs {
root(
KotlinLangOrg, """
$SCRIPT_PATH
""".trimIndent()
)
root(KotlinLangOrg, "$SCRIPT_PATH/")
cleanCheckout = true
showDependenciesChanges = true
}
Expand Down
3 changes: 2 additions & 1 deletion .teamcity/builds/apiReferences/BuildApiPages.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package builds.apiReferences

import BuildParams.DOKKA_TEMPLATES_VERSION
import builds.SCRIPT_PATH
import builds.scriptDistAnalyze
import jetbrains.buildServer.configs.kotlin.BuildStep
import jetbrains.buildServer.configs.kotlin.BuildSteps
Expand Down Expand Up @@ -39,7 +40,7 @@ abstract class BuildApiPages(
artifactRules = "$pagesRoot/** => pages.zip"

vcs {
root(KotlinLangOrg, "scripts/doindex/")
root(KotlinLangOrg, "$SCRIPT_PATH/")
}

params {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package builds.apiReferences.stdlib

import BuildParams.KOTLIN_CORE_API_BUILD_ID
import builds.SCRIPT_PATH
import builds.apiReferences.scriptGenerateSitemap
import builds.apiReferences.scriptNoRobots
import jetbrains.buildServer.configs.kotlin.AbsoluteId
import jetbrains.buildServer.configs.kotlin.BuildType
import jetbrains.buildServer.configs.kotlin.buildSteps.script
import vcsRoots.KotlinLangOrg

private const val PAGES_ROOT = "dist/api/core"

Expand All @@ -14,11 +16,7 @@ object BuildStdlibApiReference : BuildType({
description = "Build pages for Kotlin Core API"

vcs {
root(
vcsRoots.KotlinLangOrg, """
scripts/doindex/
""".trimIndent()
)
root(KotlinLangOrg, "$SCRIPT_PATH/")
}

artifactRules = """
Expand Down
56 changes: 41 additions & 15 deletions .teamcity/builds/kotlinlang/buidTypes/PdfGenerator.kt
Original file line number Diff line number Diff line change
@@ -1,46 +1,72 @@
package builds.kotlinlang.buidTypes

import builds.SCRIPT_PATH
import builds.kotlinlang.templates.DockerImageBuilder
import jetbrains.buildServer.configs.kotlin.AbsoluteId
import jetbrains.buildServer.configs.kotlin.BuildType
import jetbrains.buildServer.configs.kotlin.FailureAction
import jetbrains.buildServer.configs.kotlin.buildSteps.script


object PdfGenerator : BuildType({
name = "PDF Generator"
description = "Build PDF reference https://kotlinlang.org/docs/"

templates(DockerImageBuilder)

artifactRules = "pdf/kotlin-docs.pdf => kotlin-docs.pdf"
artifactRules = """
dist/docs/pdf.html
pdf/kotlin-docs.pdf
""".trimIndent()

requirements {
doesNotContain("docker.server.osType", "windows")
}

steps {
script {
id = "script-dist-pdf-html"
name = "Generate pdf.html"
//language=bash
scriptContent = """
#!/bin/sh
set -e
npm install
npm run generate-pdf
""".trimIndent()
dockerImage = "node:22-alpine"
workingDir = SCRIPT_PATH
}
script {
name = "Generate PDF"
//language=sh
scriptContent = """
#!/bin/bash

mv ./dist/docs/pdfSourceKR.html ./dist/docs/pdf.html
# install legacy wkhtmltopdf deps
apt update
apt install -y xfonts-75dpi xfonts-100dpi libjpeg62-turbo xfonts-base
wget https://deb.debian.org/debian/pool/main/o/openssl/libssl1.1_1.1.1w-0+deb11u1_amd64.deb
wget https://deb.debian.org/debian/pool/main/o/openssl/libssl-dev_1.1.1w-0+deb11u1_amd64.deb
wget https://deb.debian.org/debian/pool/main/o/openssl/openssl_1.1.1w-0+deb11u1_amd64.deb
dpkg -i libssl1.1_1.1.1w-0+deb11u1_amd64.deb libssl-dev_1.1.1w-0+deb11u1_amd64.deb openssl_1.1.1w-0+deb11u1_amd64.deb
ln -s /usr/lib/x86_64-linux-gnu/libjpeg.so.62 /usr/lib/x86_64-linux-gnu/libjpeg.so.8
# refresh wkhtmltopdf
wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.buster_amd64.deb
dpkg -i wkhtmltox_0.12.6-1.buster_amd64.deb

## refresh packages
pip install -r requirements.txt

python kotlin-website.py reference-pdf
""".trimIndent()
dockerImage = "%dep.Kotlin_KotlinSites_Builds_KotlinlangOrg_BuildPythonContainer.kotlin-website-image%"
dockerImage = "python:3.9"
}
}

dependencies {
dependency(AbsoluteId("Documentation_TransitioningProducts_KotlinReferenceWithCoroutines")) {
dependency(BuildReferenceDocs) {
snapshot {
onDependencyFailure = FailureAction.FAIL_TO_START
onDependencyCancel = FailureAction.CANCEL
}

artifacts {
cleanDestination = true
artifactRules = """
webHelpImages.zip!** => dist/docs/images
pdfSourceKR.html => dist/docs/
""".trimIndent()
artifactRules = "+:docs.zip!** => dist/docs/"
}
}
}
Expand Down
23 changes: 21 additions & 2 deletions pdf/webhelp.css
Original file line number Diff line number Diff line change
Expand Up @@ -210,18 +210,37 @@ h3 {
line-height:28px;
line-height:var(--wt-h3-line-height, 28px);

font-size: 14px;
font-size: 18px;
font-style: normal;
font-weight: 700;
line-height: 24px;
letter-spacing: 0px;

margin-bottom: 8px;
}
@media print {
h3 {
page-break-inside: avoid;
page-break-after: avoid;
}
}

h4 {
color:#27282c;
font-family:system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Droid Sans', 'Helvetica Neue', Arial, sans-serif;
font-size: 16px;
font-style: normal;
font-weight: 700;
line-height: 24px;
letter-spacing: 0;
margin-bottom: 8px;
}
@media print {
h4 {
page-break-inside: avoid;
page-break-after: avoid;
}
}
p, a, li {
letter-spacing:normal;
color:rgba(39, 40, 44, 0.70);
Expand Down Expand Up @@ -287,7 +306,7 @@ table th {
.list {
margin-left: 18px;
}
.list._ul {
.list._ul, .list._bullet {
list-style: disc;
}
.list._decimal {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"type": "module",
"scripts": {
"generate-metadata": "tsx analyzer/index.ts",
"build": "tsc --outDir ../pages-js --project tsconfig.json"
"generate-pdf": "tsx pdf/index.ts",
"build": "tsc --outDir ../dist --project tsconfig.json"
},
"dependencies": {
"@types/fast-levenshtein": "^0.0.4",
Expand Down
52 changes: 52 additions & 0 deletions scripts/dist/pdf/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { dirname, join } from 'node:path';
import { EventEmitter } from 'events';

import { DIST_FOLDER } from '../lib/files/index.js';
import { appendFile, open, readFile } from 'node:fs/promises';
import { processTocToUrls, Result } from './lib.js';
import { newTaskExecutor } from '../lib/pool.js';

console.time('Data successfully built');

const TASK_PATH = import.meta.dirname + '/task';

EventEmitter.defaultMaxListeners = 15;

const DOCS_PATH = join(DIST_FOLDER, 'docs');
const TOC_PATH = join(DOCS_PATH, 'HelpTOC.json');

const TOC = JSON.parse(await readFile(TOC_PATH, { encoding: 'utf-8' }));

const nodes = new Map(
(await processTocToUrls(TOC))
.map(id => new URL(id, 'https://kotlinlang.org/docs/'))
.filter(url => url.hostname === 'kotlinlang.org' && dirname(url.pathname) === '/docs')
.map(url => url.pathname.slice(1))
.map(key => [key, ''])
);

if (nodes.size === 0) throw new Error('No nodes found');

async function onItem({ id, html }: Result) {
nodes.set(id, html);
}

const [pool, finish] = newTaskExecutor(
TASK_PATH, onItem
);

for (const id of nodes.keys()) {
pool.push(id);
}

await finish;

const pdfFile = await open(join(DOCS_PATH, 'pdf.html'), 'w');

for (const html of nodes.values()) {
await appendFile(pdfFile, html);
}

await pdfFile.close();

console.timeEnd('Data successfully built');
80 changes: 80 additions & 0 deletions scripts/dist/pdf/lib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { join } from 'node:path';
import { readFile } from 'node:fs/promises';
import { CheerioAPI, load } from 'cheerio';

import { FileType } from '../lib/files/type.js';
import { ROOT_DIR } from '../lib/files/index.js';

export type Result = {
id: string
html: string
}

export function isHiddenProved(relativePath: string, type?: FileType) {
return (!type || type === 'Hidden') && (
relativePath.startsWith('docs/kotlin-tour-') ||
relativePath === 'docs/multiplatform.html'
);
}

const TREE_XML_PATH = join(ROOT_DIR + '/docs/kr.tree');

let TREE_XML: CheerioAPI | null = null;

async function readTreeXMLEntities() {
if (!TREE_XML)
TREE_XML = load(await readFile(TREE_XML_PATH, { encoding: 'utf-8' }), {
xml: true
});

return TREE_XML;
}

async function getSubtreeItems(id: string) {
const $tree = await readTreeXMLEntities();
const topic = id.replace(/\.html$/, '.md');

return $tree(`toc-element[topic="${topic}"] > toc-element[topic]`).toArray().map(node => {
const file = node?.attribs?.topic?.replace(/\.(md|topic)$/, '.html');
if (!file || !isHiddenProved(`docs/${file}`)) return null;
return file;
})
.filter(Boolean);
}

export async function processTocToUrls({ topLevelIds, entities }: {
entities: { pages: Record<string, { url?: string, pages: string[] }> },
topLevelIds: string[]
}) {
const result: string[] = [];
const visited = new Set<string>();
const stack = [...topLevelIds.reverse()]; // Reverse to maintain order
const data = entities.pages;
while (stack.length) {
const id = stack.pop();

if (visited.has(id)) continue;
visited.add(id);

const page = data[id];
if (!page) continue;
const { url, pages } = page;

if (url) result.push(page.url);

if (isHiddenProved(`docs/${page.url}`)) {
const hiddenPages = await getSubtreeItems(page.url);
for (const id of hiddenPages) {
result.push(id);
}
}

if (pages?.length) {
for (let i = page.pages.length - 1; i >= 0; i--) {
stack.push(page.pages[i]);
}
}
}

return result;
}
Loading
Loading