diff --git a/pkl-cli/src/main/kotlin/org/pkl/cli/CliCommandRunner.kt b/pkl-cli/src/main/kotlin/org/pkl/cli/CliCommandRunner.kt index 58d743d1..34e3b81f 100644 --- a/pkl-cli/src/main/kotlin/org/pkl/cli/CliCommandRunner.kt +++ b/pkl-cli/src/main/kotlin/org/pkl/cli/CliCommandRunner.kt @@ -52,8 +52,6 @@ constructor( private val errStream: OutputStream = System.err, ) : CliCommand(options) { - private val normalizedSourceModule = options.normalizedSourceModules.first() - override fun doRun() { val builder = evaluatorBuilder() try { @@ -68,7 +66,7 @@ constructor( val evaluator = builder.build() evaluator.use { evaluator.evaluateCommand( - uri(normalizedSourceModule), + uri(resolvedSourceModules.first()), reservedFlagNames, reservedFlagShortNames, ) { spec -> diff --git a/pkl-cli/src/main/kotlin/org/pkl/cli/CliEvaluator.kt b/pkl-cli/src/main/kotlin/org/pkl/cli/CliEvaluator.kt index 730b108d..34496612 100644 --- a/pkl-cli/src/main/kotlin/org/pkl/cli/CliEvaluator.kt +++ b/pkl-cli/src/main/kotlin/org/pkl/cli/CliEvaluator.kt @@ -111,12 +111,11 @@ constructor( } private fun resolveOutputPaths(pathStr: String): Map { - val moduleUris = options.base.normalizedSourceModules val workingDir = options.base.normalizedWorkingDir // used just to resolve the `%{moduleName}` placeholder val moduleResolver = ModuleResolver(moduleKeyFactories(ModulePathResolver.empty())) - return moduleUris.associateWith { uri -> + return resolvedSourceModules.associateWith { uri -> val moduleDir: String? = IoUtils.toPath(uri)?.let { IoUtils.relativize(it.parent, workingDir).toString().ifEmpty { "." } @@ -192,7 +191,7 @@ constructor( } } else { var outputWritten = false - for (moduleUri in options.base.normalizedSourceModules) { + for (moduleUri in resolvedSourceModules) { val moduleSource = toModuleSource(moduleUri, inputStream) if (options.expression != null) { val output = evaluator.evaluateExpressionString(moduleSource, options.expression) diff --git a/pkl-cli/src/main/kotlin/org/pkl/cli/CliImportAnalyzer.kt b/pkl-cli/src/main/kotlin/org/pkl/cli/CliImportAnalyzer.kt index c89cd888..df4b480d 100644 --- a/pkl-cli/src/main/kotlin/org/pkl/cli/CliImportAnalyzer.kt +++ b/pkl-cli/src/main/kotlin/org/pkl/cli/CliImportAnalyzer.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,7 +66,7 @@ constructor( try { return builder .apply { - for ((idx, sourceModule) in options.base.normalizedSourceModules.withIndex()) { + for ((idx, sourceModule) in resolvedSourceModules.withIndex()) { addExternalProperty("pkl.analyzeImports.$idx", sourceModule.toString()) } } diff --git a/pkl-cli/src/main/kotlin/org/pkl/cli/CliTestRunner.kt b/pkl-cli/src/main/kotlin/org/pkl/cli/CliTestRunner.kt index 74a31a2e..b18e5bff 100644 --- a/pkl-cli/src/main/kotlin/org/pkl/cli/CliTestRunner.kt +++ b/pkl-cli/src/main/kotlin/org/pkl/cli/CliTestRunner.kt @@ -47,7 +47,7 @@ constructor( private fun evalTest(builder: EvaluatorBuilder) { val sources = - options.normalizedSourceModules.ifEmpty { project?.tests?.map { it.toUri() } } + resolvedSourceModules.ifEmpty { project?.tests?.map { it.toUri() } } ?: // keep in sync with error message thrown by clikt throw CliException( diff --git a/pkl-cli/src/main/kotlin/org/pkl/cli/commands/AnalyzeCommand.kt b/pkl-cli/src/main/kotlin/org/pkl/cli/commands/AnalyzeCommand.kt index 88001c9b..9062db87 100644 --- a/pkl-cli/src/main/kotlin/org/pkl/cli/commands/AnalyzeCommand.kt +++ b/pkl-cli/src/main/kotlin/org/pkl/cli/commands/AnalyzeCommand.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.pkl.cli.commands import com.github.ajalt.clikt.core.Context -import com.github.ajalt.clikt.core.NoOpCliktCommand import com.github.ajalt.clikt.core.subcommands import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.types.path @@ -24,9 +23,10 @@ import java.nio.file.Path import org.pkl.cli.CliImportAnalyzer import org.pkl.cli.CliImportAnalyzerOptions import org.pkl.commons.cli.commands.ModulesCommand +import org.pkl.commons.cli.commands.NoOpCommand import org.pkl.commons.cli.commands.single -class AnalyzeCommand : NoOpCliktCommand(name = "analyze") { +class AnalyzeCommand : NoOpCommand(name = "analyze") { override fun help(context: Context) = "Commands related to static analysis" override fun helpEpilog(context: Context) = "For more information, visit $helpLink" diff --git a/pkl-cli/src/main/kotlin/org/pkl/cli/commands/ProjectCommand.kt b/pkl-cli/src/main/kotlin/org/pkl/cli/commands/ProjectCommand.kt index b78922d4..b5e48fa2 100644 --- a/pkl-cli/src/main/kotlin/org/pkl/cli/commands/ProjectCommand.kt +++ b/pkl-cli/src/main/kotlin/org/pkl/cli/commands/ProjectCommand.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ package org.pkl.cli.commands import com.github.ajalt.clikt.completion.CompletionCandidates import com.github.ajalt.clikt.core.Context -import com.github.ajalt.clikt.core.NoOpCliktCommand import com.github.ajalt.clikt.core.subcommands import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.arguments.multiple @@ -30,10 +29,11 @@ import java.nio.file.Path import org.pkl.cli.CliProjectPackager import org.pkl.cli.CliProjectResolver import org.pkl.commons.cli.commands.BaseCommand +import org.pkl.commons.cli.commands.NoOpCommand import org.pkl.commons.cli.commands.TestOptions import org.pkl.commons.cli.commands.single -class ProjectCommand : NoOpCliktCommand(name = "project") { +class ProjectCommand : NoOpCommand(name = "project") { override fun help(context: Context) = "Run commands related to projects" override fun helpEpilog(context: Context) = "For more information, visit $helpLink" diff --git a/pkl-cli/src/main/kotlin/org/pkl/cli/commands/RootCommand.kt b/pkl-cli/src/main/kotlin/org/pkl/cli/commands/RootCommand.kt index df14594b..8bf8c06e 100644 --- a/pkl-cli/src/main/kotlin/org/pkl/cli/commands/RootCommand.kt +++ b/pkl-cli/src/main/kotlin/org/pkl/cli/commands/RootCommand.kt @@ -17,21 +17,22 @@ package org.pkl.cli.commands import com.github.ajalt.clikt.completion.CompletionCommand import com.github.ajalt.clikt.core.Context -import com.github.ajalt.clikt.core.NoOpCliktCommand import com.github.ajalt.clikt.core.context import com.github.ajalt.clikt.core.subcommands +import org.pkl.commons.cli.commands.NoOpCommand import org.pkl.commons.cli.commands.installCommonOptions import org.pkl.core.Release internal val helpLink = "${Release.current().documentation.homepage}pkl-cli/index.html#usage" -class RootCommand : NoOpCliktCommand(name = "pkl") { +class RootCommand : NoOpCommand(name = "pkl") { override val printHelpOnEmptyArgs = true override fun helpEpilog(context: Context) = "For more information, visit $helpLink" init { context { + readArgumentFile = null suggestTypoCorrection = { given, possible -> if (!given.startsWith("-")) { registeredSubcommands().map { it.commandName } diff --git a/pkl-cli/src/main/kotlin/org/pkl/cli/commands/RunCommand.kt b/pkl-cli/src/main/kotlin/org/pkl/cli/commands/RunCommand.kt index 56d6a7cd..baa0d37b 100644 --- a/pkl-cli/src/main/kotlin/org/pkl/cli/commands/RunCommand.kt +++ b/pkl-cli/src/main/kotlin/org/pkl/cli/commands/RunCommand.kt @@ -49,7 +49,11 @@ class RunCommand : BaseCommand(name = "run", helpLink = helpLink) { private val showHelp by option("-h", "--help", help = "Show this message and exit").flag() val module: URI? by - argument(name = "module", completionCandidates = CompletionCandidates.Path) + argument( + name = "module", + help = "Root pkl:Command module to invoke.", + completionCandidates = CompletionCandidates.Path, + ) .convert { BaseOptions.parseModuleName(it) } .optional() diff --git a/pkl-codegen-java/src/main/kotlin/org/pkl/codegen/java/CliJavaCodeGenerator.kt b/pkl-codegen-java/src/main/kotlin/org/pkl/codegen/java/CliJavaCodeGenerator.kt index cc4d58fc..f93c66d6 100644 --- a/pkl-codegen-java/src/main/kotlin/org/pkl/codegen/java/CliJavaCodeGenerator.kt +++ b/pkl-codegen-java/src/main/kotlin/org/pkl/codegen/java/CliJavaCodeGenerator.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ class CliJavaCodeGenerator(private val options: CliJavaCodeGeneratorOptions) : val builder = evaluatorBuilder() try { builder.build().use { evaluator -> - for (moduleUri in options.base.normalizedSourceModules) { + for (moduleUri in resolvedSourceModules) { val schema = evaluator.evaluateSchema(ModuleSource.uri(moduleUri)) val codeGenerator = JavaCodeGenerator(schema, options.toJavaCodeGeneratorOptions()) try { diff --git a/pkl-codegen-kotlin/src/main/kotlin/org/pkl/codegen/kotlin/CliKotlinCodeGenerator.kt b/pkl-codegen-kotlin/src/main/kotlin/org/pkl/codegen/kotlin/CliKotlinCodeGenerator.kt index 16b7d377..d7089886 100644 --- a/pkl-codegen-kotlin/src/main/kotlin/org/pkl/codegen/kotlin/CliKotlinCodeGenerator.kt +++ b/pkl-codegen-kotlin/src/main/kotlin/org/pkl/codegen/kotlin/CliKotlinCodeGenerator.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,7 @@ class CliKotlinCodeGenerator(private val options: CliKotlinCodeGeneratorOptions) try { builder.build().use { evaluator -> - for (moduleUri in options.base.normalizedSourceModules) { + for (moduleUri in resolvedSourceModules) { val schema = evaluator.evaluateSchema(ModuleSource.uri(moduleUri)) val codeGenerator = KotlinCodeGenerator(schema, options.toKotlinCodeGeneratorOptions()) try { diff --git a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliBaseOptions.kt b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliBaseOptions.kt index fda71e53..b3d6b314 100644 --- a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliBaseOptions.kt +++ b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliBaseOptions.kt @@ -175,11 +175,19 @@ data class CliBaseOptions( /** [rootDir] after normalization. */ val normalizedRootDir: Path? = rootDir?.let(normalizedWorkingDir::resolve) + /** The effective project directory, if exists. */ + val normalizedProjectFile: Path? by lazy { + projectDir?.resolve(ProjectDependenciesManager.PKL_PROJECT_FILENAME) + ?: normalizedWorkingDir.getProjectFile(rootDir) + } + /** [sourceModules] after normalization. */ val normalizedSourceModules: List = sourceModules .map { uri -> - if (uri.isAbsolute) uri else IoUtils.resolve(normalizedWorkingDir.toUri(), uri) + if (uri.isAbsolute) uri + else if (uri.path.startsWith("@") && !noProject && normalizedProjectFile != null) uri + else IoUtils.resolve(normalizedWorkingDir.toUri(), uri) } // sort modules to make cli output independent of source module order .sorted() @@ -195,12 +203,6 @@ data class CliBaseOptions( /** [moduleCacheDir] after normalization. */ val normalizedModuleCacheDir: Path? = moduleCacheDir?.let(normalizedWorkingDir::resolve) - /** The effective project directory, if exists. */ - val normalizedProjectFile: Path? by lazy { - projectDir?.resolve(ProjectDependenciesManager.PKL_PROJECT_FILENAME) - ?: normalizedWorkingDir.getProjectFile(rootDir) - } - /** [caCertificates] after normalization. */ val normalizedCaCertificates: List = caCertificates.map(normalizedWorkingDir::resolve) } diff --git a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliCommand.kt b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliCommand.kt index 9d875454..856d2231 100644 --- a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliCommand.kt +++ b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliCommand.kt @@ -86,6 +86,35 @@ abstract class CliCommand(protected val cliOptions: CliBaseOptions) { } } + protected fun resolveModuleUri(uri: URI): URI = + if (uri.isAbsolute) uri + else { // must be @dep/mod.pkl notation!! + if (!uri.path.startsWith('@')) + throw CliBugException( + RuntimeException("tried to resolve project URI `$uri` with no @ prefix") + ) + if (project == null) + throw CliBugException( + RuntimeException("tried to resolve project URI `$uri` with no project present") + ) + val dep = uri.path.substringBefore('/').drop(1) + val path = uri.path.dropWhile { it != '/' } + if (path.isEmpty()) throw CliException("Invalid project dependency URI `$uri`.") + + val remoteDep = + project!!.dependencies.remoteDependencies()[dep] + ?: if (project!!.dependencies.localDependencies().containsKey(dep)) + throw CliException( + "Only remote project dependencies may be referenced using @-notation. Dependency `@$dep` is a local dependency." + ) + else throw CliException("Project does not contain dependency `@$dep`.") + remoteDep.packageUri.toPackageAssetUri(path).uri + } + + protected val resolvedSourceModules: List = + if (project == null) cliOptions.normalizedSourceModules + else cliOptions.normalizedSourceModules.map(::resolveModuleUri) + protected fun loadProject(projectFile: Path): Project { val securityManager = SecurityManagers.standard( diff --git a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/BaseCommand.kt b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/BaseCommand.kt index 252ffdcf..d440639a 100644 --- a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/BaseCommand.kt +++ b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/BaseCommand.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,9 +17,14 @@ package org.pkl.commons.cli.commands import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.Context +import com.github.ajalt.clikt.core.context import com.github.ajalt.clikt.parameters.groups.provideDelegate abstract class BaseCommand(name: String, private val helpLink: String) : CliktCommand(name = name) { + init { + context { readArgumentFile = null } + } + abstract val helpString: String override fun help(context: Context) = helpString diff --git a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/NoOpCommand.kt b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/NoOpCommand.kt new file mode 100644 index 00000000..47d34708 --- /dev/null +++ b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/NoOpCommand.kt @@ -0,0 +1,25 @@ +/* + * Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pkl.commons.cli.commands + +import com.github.ajalt.clikt.core.NoOpCliktCommand +import com.github.ajalt.clikt.core.context + +open class NoOpCommand(name: String? = null) : NoOpCliktCommand(name) { + init { + context { readArgumentFile = null } + } +} diff --git a/pkl-commons-cli/src/test/kotlin/org/pkl/commons/cli/CliCommandTest.kt b/pkl-commons-cli/src/test/kotlin/org/pkl/commons/cli/CliCommandTest.kt index d5a2eea5..9fd07c6e 100644 --- a/pkl-commons-cli/src/test/kotlin/org/pkl/commons/cli/CliCommandTest.kt +++ b/pkl-commons-cli/src/test/kotlin/org/pkl/commons/cli/CliCommandTest.kt @@ -16,11 +16,18 @@ package org.pkl.commons.cli import com.github.ajalt.clikt.core.parse +import java.net.URI +import java.nio.file.Path +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.writeText import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.io.TempDir import org.pkl.commons.cli.commands.BaseCommand import org.pkl.core.SecurityManagers +@OptIn(ExperimentalPathApi::class) class CliCommandTest { class CliTest(options: CliBaseOptions) : CliCommand(options) { @@ -28,6 +35,7 @@ class CliCommandTest { val myAllowedResources = allowedResources val myAllowedModules = allowedModules + val myResolvedSourceModules = resolvedSourceModules } private val cmd = @@ -55,7 +63,7 @@ class CliCommandTest { "scheme+ext=reader5 with args", ) ) - val opts = cmd.baseOptions.baseOptions(emptyList(), null, true) + val opts = cmd.baseOptions.baseOptions(emptyList(), testMode = true) val cliTest = CliTest(opts) assertThat(cliTest.myAllowedModules.map { it.pattern() }) .isEqualTo( @@ -68,4 +76,112 @@ class CliCommandTest { listOf("\\Qscheme1:\\E", "\\Qscheme2:\\E", "\\Qscheme+ext:\\E") ) } + + @Test + fun `@-notation package URIs - treated as relative paths when no project present`( + @TempDir tempDir: Path + ) { + cmd.parse(arrayOf("--working-dir=$tempDir")) + val opts = cmd.baseOptions.baseOptions(listOf(URI("@foo/bar.pkl")), testMode = true) + val cliTest = CliTest(opts) + assertThat(cliTest.myResolvedSourceModules) + .isEqualTo(listOf(tempDir.toUri().resolve("@foo/bar.pkl"))) + } + + @Test + fun `@-notation package URIs - empty paths are rejected`(@TempDir tempDir: Path) { + tempDir + .resolve("PklProject") + .writeText( + """ + amends "pkl:Project" + """ + .trimIndent() + ) + cmd.parse(arrayOf("--working-dir=$tempDir")) + val opts = cmd.baseOptions.baseOptions(listOf(URI("@no.slash")), testMode = true) + val exc = assertThrows { CliTest(opts) } + assertThat(exc.message).isEqualTo("Invalid project dependency URI `@no.slash`.") + } + + @Test + fun `@-notation package URIs - missing dependencies are rejected`(@TempDir tempDir: Path) { + tempDir + .resolve("PklProject") + .writeText( + """ + amends "pkl:Project" + """ + .trimIndent() + ) + cmd.parse(arrayOf("--working-dir=$tempDir")) + val opts = cmd.baseOptions.baseOptions(listOf(URI("@foo/bar.pkl")), testMode = true) + val exc = assertThrows { CliTest(opts) } + assertThat(exc.message).isEqualTo("Project does not contain dependency `@foo`.") + } + + @Test + fun `@-notation package URIs - local dependencies are rejected`( + @TempDir tempDir: Path, + @TempDir depDir: Path, + ) { + depDir + .resolve("PklProject") + .writeText( + """ + amends "pkl:Project" + + package { + name = "foo" + baseUri = "package://example.com/foo" + version = "0.0.1" + packageZipUrl = "https://example.com/foo@\(version).zip" + } + """ + .trimIndent() + ) + + tempDir + .resolve("PklProject") + .writeText( + """ + amends "pkl:Project" + + dependencies { + ["foo"] = import("$depDir/PklProject") + } + """ + .trimIndent() + ) + cmd.parse(arrayOf("--working-dir=$tempDir")) + val opts = cmd.baseOptions.baseOptions(listOf(URI("@foo/bar.pkl")), testMode = true) + val exc = assertThrows { CliTest(opts) } + assertThat(exc.message) + .isEqualTo( + "Only remote project dependencies may be referenced using @-notation. Dependency `@foo` is a local dependency." + ) + } + + @Test + fun `@-notation package URIs - remote dependencies are resolved`(@TempDir tempDir: Path) { + tempDir + .resolve("PklProject") + .writeText( + """ + amends "pkl:Project" + + dependencies { + ["foo"] { + uri = "package://example.com/foo@1.2.3" + } + } + """ + .trimIndent() + ) + cmd.parse(arrayOf("--working-dir=$tempDir")) + val opts = cmd.baseOptions.baseOptions(listOf(URI("@foo/bar.pkl")), testMode = true) + val cliTest = CliTest(opts) + assertThat(cliTest.myResolvedSourceModules) + .isEqualTo(listOf(tempDir.toUri().resolve("package://example.com/foo@1.2.3#/bar.pkl"))) + } } diff --git a/pkl-doc/src/main/kotlin/org/pkl/doc/CliDocGenerator.kt b/pkl-doc/src/main/kotlin/org/pkl/doc/CliDocGenerator.kt index 265017fe..6e0a90e7 100644 --- a/pkl-doc/src/main/kotlin/org/pkl/doc/CliDocGenerator.kt +++ b/pkl-doc/src/main/kotlin/org/pkl/doc/CliDocGenerator.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -159,7 +159,7 @@ class CliDocGenerator( val regularModuleUris = mutableListOf() val pklProjectPaths = mutableSetOf() val packageUris = mutableListOf() - for (moduleUri in options.base.normalizedSourceModules) { + for (moduleUri in resolvedSourceModules) { if (moduleUri.scheme == "file") { val dir = moduleUri.toPath().parent val projectFile = dir.getProjectFile(options.base.normalizedRootDir)