mirror of
https://github.com/apple/pkl.git
synced 2026-04-20 15:31:28 +02:00
Encode filepaths to be safe on Windows
This changes the file paths to use characters that are safe for Windows. Channges the output of the following: * Package cache directory * Generated pkl-doc files * Kotlin generated code Unsafe characters are encoded as (<hex>). For example, the colon character `:` is encoded as `(3a)`. Additionally, this changes the cache directory prefix (package-1 to package-2). Follows the design of https://github.com/apple/pkl-evolution/pull/3
This commit is contained in:
@@ -121,7 +121,7 @@ class DocGenerator(
|
||||
|
||||
private fun createSymlinks(currentPackagesData: List<PackageData>) {
|
||||
for (packageData in currentPackagesData) {
|
||||
val basePath = outputDir.resolve(packageData.ref.pkg)
|
||||
val basePath = outputDir.resolve(packageData.ref.pkg.pathEncoded)
|
||||
val src = basePath.resolve(packageData.ref.version)
|
||||
val dest = basePath.resolve("current")
|
||||
if (dest.exists() && dest.isSameFileAs(src)) continue
|
||||
|
||||
@@ -203,9 +203,9 @@ data class DocPackageInfo(
|
||||
when {
|
||||
!moduleName.startsWith(prefix) -> null
|
||||
else -> {
|
||||
val modulePath = moduleName.substring(prefix.length).replace('.', '/')
|
||||
val modulePath = moduleName.substring(prefix.length).replace('.', '/').pathEncoded
|
||||
if (documentation == null) {
|
||||
"$name/$version/$modulePath/index.html".toUri()
|
||||
"${name.pathEncoded}/$version/$modulePath/index.html".toUri()
|
||||
} else {
|
||||
documentation.resolve("$modulePath/index.html")
|
||||
}
|
||||
|
||||
@@ -71,10 +71,7 @@ internal sealed class DocScope {
|
||||
fun resolveModuleNameToRelativeDocUrl(name: String): URI? =
|
||||
resolveModuleNameToDocUrl(name)?.let { IoUtils.relativize(it, url) }
|
||||
|
||||
abstract fun resolveModuleNameToSourceUrl(
|
||||
name: String,
|
||||
sourceLocation: Member.SourceLocation
|
||||
): URI?
|
||||
abstract fun resolveModuleNameToSourceUrl(name: String, sourceLocation: SourceLocation): URI?
|
||||
|
||||
/** Resolves the given method name relative to this scope. */
|
||||
abstract fun resolveMethod(name: String): MethodScope?
|
||||
@@ -207,10 +204,7 @@ internal class SiteScope(
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun resolveModuleNameToSourceUrl(
|
||||
name: String,
|
||||
sourceLocation: Member.SourceLocation
|
||||
): URI? =
|
||||
override fun resolveModuleNameToSourceUrl(name: String, sourceLocation: SourceLocation): URI? =
|
||||
when {
|
||||
name.startsWith("pkl.") -> {
|
||||
val path = "/stdlib/${name.substring(4)}.pkl"
|
||||
@@ -253,7 +247,9 @@ internal class PackageScope(
|
||||
private val moduleScopes: Map<String, ModuleScope> by lazy {
|
||||
modules.associate { module ->
|
||||
val docUrl =
|
||||
url.resolve(getModulePath(module.moduleName, modulePrefix).uriEncoded + "/index.html")
|
||||
url.resolve(
|
||||
getModulePath(module.moduleName, modulePrefix).pathEncoded.uriEncoded + "/index.html"
|
||||
)
|
||||
module.moduleName to ModuleScope(module, docUrl, this)
|
||||
}
|
||||
}
|
||||
@@ -262,9 +258,11 @@ internal class PackageScope(
|
||||
ModuleScope(pklBaseModule, resolveModuleNameToDocUrl("pkl.base")!!, null)
|
||||
}
|
||||
|
||||
override val url: URI by lazy { parent.url.resolve("./$name/$version/index.html") }
|
||||
override val url: URI by lazy { parent.url.resolve("./${name.pathEncoded}/$version/index.html") }
|
||||
|
||||
override val dataUrl: URI by lazy { parent.url.resolve("./data/$name/$version/index.js") }
|
||||
override val dataUrl: URI by lazy {
|
||||
parent.url.resolve("./data/${name.pathEncoded}/$version/index.js")
|
||||
}
|
||||
|
||||
fun getModule(name: String): ModuleScope = moduleScopes.getValue(name)
|
||||
|
||||
@@ -387,11 +385,11 @@ internal class ClassScope(
|
||||
override val url: URI by lazy {
|
||||
// `isModuleClass` distinction is relevant when this scope is a link target
|
||||
if (clazz.isModuleClass) parentUrl
|
||||
else parentUrl.resolve("${clazz.simpleName.uriEncodedComponent}.html")
|
||||
else parentUrl.resolve("${clazz.simpleName.pathEncoded.uriEncodedComponent}.html")
|
||||
}
|
||||
|
||||
override val dataUrl: URI by lazy {
|
||||
parent!!.dataUrl.resolve("${clazz.simpleName.uriEncodedComponent}.js")
|
||||
parent!!.dataUrl.resolve("${clazz.simpleName.pathEncoded.uriEncodedComponent}.js")
|
||||
}
|
||||
|
||||
override fun getMethod(name: String): MethodScope? =
|
||||
@@ -403,10 +401,8 @@ internal class ClassScope(
|
||||
override fun resolveModuleNameToDocUrl(name: String): URI? =
|
||||
parent!!.resolveModuleNameToDocUrl(name)
|
||||
|
||||
override fun resolveModuleNameToSourceUrl(
|
||||
name: String,
|
||||
sourceLocation: Member.SourceLocation
|
||||
): URI? = parent!!.resolveModuleNameToSourceUrl(name, sourceLocation)
|
||||
override fun resolveModuleNameToSourceUrl(name: String, sourceLocation: SourceLocation): URI? =
|
||||
parent!!.resolveModuleNameToSourceUrl(name, sourceLocation)
|
||||
|
||||
override fun resolveMethod(name: String): MethodScope? =
|
||||
clazz.methods[name]?.let { MethodScope(it, this) }
|
||||
@@ -438,7 +434,7 @@ internal class TypeAliasScope(
|
||||
// only used for page scopes
|
||||
throw UnsupportedOperationException("resolveModuleNameToDocUrl")
|
||||
|
||||
override fun resolveModuleNameToSourceUrl(name: String, sourceLocation: Member.SourceLocation) =
|
||||
override fun resolveModuleNameToSourceUrl(name: String, sourceLocation: SourceLocation) =
|
||||
// only used for page scopes
|
||||
throw UnsupportedOperationException("resolveModuleNameToSourceUrl")
|
||||
|
||||
@@ -464,7 +460,7 @@ internal class MethodScope(val method: PClass.Method, override val parent: DocSc
|
||||
// only used for page scopes
|
||||
throw UnsupportedOperationException("resolveModuleNameToDocUrl")
|
||||
|
||||
override fun resolveModuleNameToSourceUrl(name: String, sourceLocation: Member.SourceLocation) =
|
||||
override fun resolveModuleNameToSourceUrl(name: String, sourceLocation: SourceLocation) =
|
||||
// only used for page scopes
|
||||
throw UnsupportedOperationException("resolveModuleNameToSourceUrl")
|
||||
|
||||
@@ -494,7 +490,7 @@ internal class PropertyScope(
|
||||
// only used for page scopes
|
||||
throw UnsupportedOperationException("resolveModuleNameToDocUrl")
|
||||
|
||||
override fun resolveModuleNameToSourceUrl(name: String, sourceLocation: Member.SourceLocation) =
|
||||
override fun resolveModuleNameToSourceUrl(name: String, sourceLocation: SourceLocation) =
|
||||
// only used for page scopes
|
||||
throw UnsupportedOperationException("resolveModuleNameToSourceUrl")
|
||||
|
||||
@@ -525,7 +521,7 @@ internal class ParameterScope(val name: String, override val parent: DocScope) :
|
||||
// only used for page scopes
|
||||
throw UnsupportedOperationException("resolveModuleNameToDocUrl")
|
||||
|
||||
override fun resolveModuleNameToSourceUrl(name: String, sourceLocation: Member.SourceLocation) =
|
||||
override fun resolveModuleNameToSourceUrl(name: String, sourceLocation: SourceLocation) =
|
||||
// only used for page scopes
|
||||
throw UnsupportedOperationException("resolveModuleNameToSourceUrl")
|
||||
|
||||
|
||||
@@ -37,9 +37,11 @@ import org.pkl.core.util.IoUtils
|
||||
internal class PackageDataGenerator(private val outputDir: Path) {
|
||||
fun generate(pkg: DocPackage) {
|
||||
val path =
|
||||
outputDir.resolve(pkg.name).resolve(pkg.version).resolve("package-data.json").apply {
|
||||
createParentDirectories()
|
||||
}
|
||||
outputDir
|
||||
.resolve(pkg.name.pathEncoded)
|
||||
.resolve(pkg.version)
|
||||
.resolve("package-data.json")
|
||||
.apply { createParentDirectories() }
|
||||
PackageData(pkg).write(path)
|
||||
}
|
||||
|
||||
|
||||
@@ -82,60 +82,71 @@ internal class RuntimeDataGenerator(
|
||||
}
|
||||
|
||||
private fun writePackageFile(ref: PackageRef) {
|
||||
outputDir.resolve("data/${ref.pkg}/${ref.version}/index.js").jsonWriter().use { writer ->
|
||||
writer.isLenient = true
|
||||
writer.writeLinks(
|
||||
HtmlConstants.KNOWN_VERSIONS,
|
||||
packageVersions.getOrDefault(ref.pkg, setOf()).sortedWith(descendingVersionComparator),
|
||||
{ it },
|
||||
{ if (it == ref.version) null else ref.copy(version = it).pageUrlRelativeTo(ref) },
|
||||
{ if (it == ref.version) CssConstants.CURRENT_VERSION else null }
|
||||
)
|
||||
writer.writeLinks(
|
||||
HtmlConstants.KNOWN_USAGES,
|
||||
packageUsages.getOrDefault(ref, setOf()).packagesWithHighestVersions().sortedBy { it.pkg },
|
||||
PackageRef::pkg,
|
||||
{ it.pageUrlRelativeTo(ref) },
|
||||
{ null }
|
||||
)
|
||||
}
|
||||
outputDir
|
||||
.resolve("data/${ref.pkg.pathEncoded}/${ref.version.pathEncoded}/index.js")
|
||||
.jsonWriter()
|
||||
.use { writer ->
|
||||
writer.isLenient = true
|
||||
writer.writeLinks(
|
||||
HtmlConstants.KNOWN_VERSIONS,
|
||||
packageVersions.getOrDefault(ref.pkg, setOf()).sortedWith(descendingVersionComparator),
|
||||
{ it },
|
||||
{ if (it == ref.version) null else ref.copy(version = it).pageUrlRelativeTo(ref) },
|
||||
{ if (it == ref.version) CssConstants.CURRENT_VERSION else null }
|
||||
)
|
||||
writer.writeLinks(
|
||||
HtmlConstants.KNOWN_USAGES,
|
||||
packageUsages.getOrDefault(ref, setOf()).packagesWithHighestVersions().sortedBy {
|
||||
it.pkg
|
||||
},
|
||||
PackageRef::pkg,
|
||||
{ it.pageUrlRelativeTo(ref) },
|
||||
{ null }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeModuleFile(ref: ModuleRef) {
|
||||
outputDir.resolve("data/${ref.pkg}/${ref.version}/${ref.module}/index.js").jsonWriter().use {
|
||||
writer ->
|
||||
writer.isLenient = true
|
||||
writer.writeLinks(
|
||||
HtmlConstants.KNOWN_VERSIONS,
|
||||
moduleVersions.getOrDefault(ref.id, setOf()).sortedWith(descendingVersionComparator),
|
||||
{ it },
|
||||
{ if (it == ref.version) null else ref.copy(version = it).pageUrlRelativeTo(ref) },
|
||||
{ if (it == ref.version) CssConstants.CURRENT_VERSION else null }
|
||||
outputDir
|
||||
.resolve(
|
||||
"data/${ref.pkg.pathEncoded}/${ref.version.pathEncoded}/${ref.module.pathEncoded}/index.js"
|
||||
)
|
||||
writer.writeLinks(
|
||||
HtmlConstants.KNOWN_USAGES,
|
||||
typeUsages.getOrDefault(ref.moduleClassRef, setOf()).typesWithHighestVersions().sortedBy {
|
||||
it.displayName
|
||||
},
|
||||
TypeRef::displayName,
|
||||
{ it.pageUrlRelativeTo(ref) },
|
||||
{ null }
|
||||
)
|
||||
writer.writeLinks(
|
||||
HtmlConstants.KNOWN_SUBTYPES,
|
||||
subtypes.getOrDefault(ref.moduleClassRef, setOf()).typesWithHighestVersions().sortedBy {
|
||||
it.displayName
|
||||
},
|
||||
TypeRef::displayName,
|
||||
{ it.pageUrlRelativeTo(ref) },
|
||||
{ null }
|
||||
)
|
||||
}
|
||||
.jsonWriter()
|
||||
.use { writer ->
|
||||
writer.isLenient = true
|
||||
writer.writeLinks(
|
||||
HtmlConstants.KNOWN_VERSIONS,
|
||||
moduleVersions.getOrDefault(ref.id, setOf()).sortedWith(descendingVersionComparator),
|
||||
{ it },
|
||||
{ if (it == ref.version) null else ref.copy(version = it).pageUrlRelativeTo(ref) },
|
||||
{ if (it == ref.version) CssConstants.CURRENT_VERSION else null }
|
||||
)
|
||||
writer.writeLinks(
|
||||
HtmlConstants.KNOWN_USAGES,
|
||||
typeUsages.getOrDefault(ref.moduleClassRef, setOf()).typesWithHighestVersions().sortedBy {
|
||||
it.displayName
|
||||
},
|
||||
TypeRef::displayName,
|
||||
{ it.pageUrlRelativeTo(ref) },
|
||||
{ null }
|
||||
)
|
||||
writer.writeLinks(
|
||||
HtmlConstants.KNOWN_SUBTYPES,
|
||||
subtypes.getOrDefault(ref.moduleClassRef, setOf()).typesWithHighestVersions().sortedBy {
|
||||
it.displayName
|
||||
},
|
||||
TypeRef::displayName,
|
||||
{ it.pageUrlRelativeTo(ref) },
|
||||
{ null }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeClassFile(ref: TypeRef) {
|
||||
outputDir
|
||||
.resolve("data/${ref.pkg}/${ref.version}/${ref.module}/${ref.type}.js")
|
||||
.resolve(
|
||||
"data/${ref.pkg.pathEncoded}/${ref.version.pathEncoded}/${ref.module.pathEncoded}/${ref.type.pathEncoded}.js"
|
||||
)
|
||||
.jsonWriter()
|
||||
.use { writer ->
|
||||
writer.isLenient = true
|
||||
|
||||
@@ -64,7 +64,7 @@ internal class SearchIndexGenerator(private val outputDir: Path) {
|
||||
fun generate(docPackage: DocPackage) {
|
||||
val path =
|
||||
outputDir
|
||||
.resolve("${docPackage.name}/${docPackage.version}/search-index.js")
|
||||
.resolve("${docPackage.name.pathEncoded}/${docPackage.version}/search-index.js")
|
||||
.createParentDirectories()
|
||||
JsonWriter(path.bufferedWriter()).use { writer ->
|
||||
writer.apply {
|
||||
|
||||
@@ -17,14 +17,13 @@ package org.pkl.doc
|
||||
|
||||
import java.io.InputStream
|
||||
import java.net.URI
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.bufferedWriter
|
||||
import kotlin.io.path.outputStream
|
||||
import org.pkl.commons.createParentDirectories
|
||||
import org.pkl.core.*
|
||||
import org.pkl.core.parser.Lexer
|
||||
import org.pkl.core.util.IoUtils
|
||||
import org.pkl.core.util.json.JsonWriter
|
||||
|
||||
// overwrites any existing file
|
||||
@@ -148,3 +147,6 @@ internal val String.asModuleName: String
|
||||
|
||||
internal val String.asIdentifier: String
|
||||
get() = Lexer.maybeQuoteIdentifier(this)
|
||||
|
||||
internal val String.pathEncoded
|
||||
get(): String = IoUtils.encodePath(this)
|
||||
|
||||
@@ -114,7 +114,7 @@ age: Int
|
||||
<div class="member-modifiers">package </div>
|
||||
</div>
|
||||
<div class="member-main">
|
||||
<div class="member-signature"><a class="name-decl" href="./localhost:0/birds/current/index.html">localhost:0/birds</a></div>
|
||||
<div class="member-signature"><a class="name-decl" href="./localhost(3a)0/birds/current/index.html">localhost:0/birds</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
@@ -125,7 +125,7 @@ age: Int
|
||||
<div class="member-modifiers">package </div>
|
||||
</div>
|
||||
<div class="member-main">
|
||||
<div class="member-signature"><a class="name-decl" href="./localhost:0/fruit/current/index.html">localhost:0/fruit</a></div>
|
||||
<div class="member-signature"><a class="name-decl" href="./localhost(3a)0/fruit/current/index.html">localhost:0/fruit</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<title>Bird (localhost:0/birds:0.5.0) • Docsite Title</title>
|
||||
<script src="../../../../scripts/pkldoc.js" defer="defer"></script>
|
||||
<script src="../../../../scripts/scroll-into-view.min.js" defer="defer"></script>
|
||||
<script src="../../../../data/localhost:0/birds/0.5.0/Bird/index.js" defer="defer"></script>
|
||||
<script src="../../../../data/localhost(3a)0/birds/0.5.0/Bird/index.js" defer="defer"></script>
|
||||
<link href="../../../../styles/pkldoc.css" media="screen" type="text/css" rel="stylesheet">
|
||||
<link rel="icon" type="image/svg+xml" href="../../../../images/favicon.svg">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="../../../../images/apple-touch-icon.png">
|
||||
@@ -4,7 +4,7 @@
|
||||
<title>allFruit (localhost:0/birds:0.5.0) • Docsite Title</title>
|
||||
<script src="../../../../scripts/pkldoc.js" defer="defer"></script>
|
||||
<script src="../../../../scripts/scroll-into-view.min.js" defer="defer"></script>
|
||||
<script src="../../../../data/localhost:0/birds/0.5.0/allFruit/index.js" defer="defer"></script>
|
||||
<script src="../../../../data/localhost(3a)0/birds/0.5.0/allFruit/index.js" defer="defer"></script>
|
||||
<link href="../../../../styles/pkldoc.css" media="screen" type="text/css" rel="stylesheet">
|
||||
<link rel="icon" type="image/svg+xml" href="../../../../images/favicon.svg">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="../../../../images/apple-touch-icon.png">
|
||||
@@ -4,7 +4,7 @@
|
||||
<title>catalog (localhost:0/birds:0.5.0) • Docsite Title</title>
|
||||
<script src="../../../../scripts/pkldoc.js" defer="defer"></script>
|
||||
<script src="../../../../scripts/scroll-into-view.min.js" defer="defer"></script>
|
||||
<script src="../../../../data/localhost:0/birds/0.5.0/catalog/index.js" defer="defer"></script>
|
||||
<script src="../../../../data/localhost(3a)0/birds/0.5.0/catalog/index.js" defer="defer"></script>
|
||||
<link href="../../../../styles/pkldoc.css" media="screen" type="text/css" rel="stylesheet">
|
||||
<link rel="icon" type="image/svg+xml" href="../../../../images/favicon.svg">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="../../../../images/apple-touch-icon.png">
|
||||
@@ -4,7 +4,7 @@
|
||||
<title>localhost:0/birds (0.5.0) • Docsite Title</title>
|
||||
<script src="../../../scripts/pkldoc.js" defer="defer"></script>
|
||||
<script src="../../../scripts/scroll-into-view.min.js" defer="defer"></script>
|
||||
<script src="../../../data/localhost:0/birds/0.5.0/index.js" defer="defer"></script>
|
||||
<script src="../../../data/localhost(3a)0/birds/0.5.0/index.js" defer="defer"></script>
|
||||
<link href="../../../styles/pkldoc.css" media="screen" type="text/css" rel="stylesheet">
|
||||
<link rel="icon" type="image/svg+xml" href="../../../images/favicon.svg">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="../../../images/apple-touch-icon.png">
|
||||
@@ -4,7 +4,7 @@
|
||||
<title>Fruit (localhost:0/fruit:1.1.0) • Docsite Title</title>
|
||||
<script src="../../../../scripts/pkldoc.js" defer="defer"></script>
|
||||
<script src="../../../../scripts/scroll-into-view.min.js" defer="defer"></script>
|
||||
<script src="../../../../data/localhost:0/fruit/1.1.0/Fruit/index.js" defer="defer"></script>
|
||||
<script src="../../../../data/localhost(3a)0/fruit/1.1.0/Fruit/index.js" defer="defer"></script>
|
||||
<link href="../../../../styles/pkldoc.css" media="screen" type="text/css" rel="stylesheet">
|
||||
<link rel="icon" type="image/svg+xml" href="../../../../images/favicon.svg">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="../../../../images/apple-touch-icon.png">
|
||||
@@ -4,7 +4,7 @@
|
||||
<title>localhost:0/fruit (1.1.0) • Docsite Title</title>
|
||||
<script src="../../../scripts/pkldoc.js" defer="defer"></script>
|
||||
<script src="../../../scripts/scroll-into-view.min.js" defer="defer"></script>
|
||||
<script src="../../../data/localhost:0/fruit/1.1.0/index.js" defer="defer"></script>
|
||||
<script src="../../../data/localhost(3a)0/fruit/1.1.0/index.js" defer="defer"></script>
|
||||
<link href="../../../styles/pkldoc.css" media="screen" type="text/css" rel="stylesheet">
|
||||
<link rel="icon" type="image/svg+xml" href="../../../images/favicon.svg">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="../../../images/apple-touch-icon.png">
|
||||
Reference in New Issue
Block a user