diff --git a/CHANGELOG.md b/CHANGELOG.md index bfa7756..65595bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file. ## Unreleased +- **Fix: `lucli run .cfm` No Longer Prints the Built-in Variables Setup:** For a `.cfm` (a template, where text outside `` is output), the built-in variables setup was prepended as plain text, so about 20 lines of setup code and an `// === Original Script Content ===` marker were printed before the template's output. The setup now runs inside `` ahead of the unchanged template, so the output is exactly what the template renders, and the built-in variables (`__scriptPath`, `ARGS`, …) are still available. `.cfs` scripts were not affected. Upstream: cybersonic/LuCLI#137. Adds `TemplatePreludeTest`. + - **Fix: Root Flags Before a Root Subcommand Under an Aliased Binary:** `wheels --timing cfml 'x'` (any root option such as `--timing`, `--verbose`, `-e prod`, `--timeout=30` before a reserved root subcommand) was handed to the module, because the reserved-token rule treated the subcommand as a module positional when it wasn't at index 1. The leading position now skips root options and their separate values, so the root subcommand runs with the flags as it does without them. Reserved tokens later in a module command are still rewritten as before. Adds `LuCLIModuleHelpPreprocessTest` cases. - **Fix: `server start --dry-run` Writes Nothing and Shows `--name`:** In a folder without `lucee.json`, the dry run wrote a default `lucee.json` into the project, and its preview showed the folder name even when `--name` was given. A dry run now loads the configuration without persisting the default (`LuceeServerConfig.loadConfig(projectDir, file, persistDefault)`), and the preview applies `--name` as a real start does. Upstream: cybersonic/LuCLI#139. Adds two `ServerCommandHandlerTest` cases. diff --git a/FORK.md b/FORK.md index 7d13fb0..c2e695f 100644 --- a/FORK.md +++ b/FORK.md @@ -87,6 +87,7 @@ No release is tagged for this queue yet; tagging needs the Wheels release owner' | 10 | wheels-dev/LuCLI [#22](https://github.com/wheels-dev/LuCLI/pull/22) (issue: upstream [cybersonic/LuCLI#140](https://github.com/cybersonic/LuCLI/issues/140)) | REPL / terminal history and `settings.json` follow the active LuCLI home instead of `user.home`; legacy `~/.lucli/settings.json` read once and copied | | 11 | wheels-dev/LuCLI [#23](https://github.com/wheels-dev/LuCLI/pull/23) (issue: upstream [cybersonic/LuCLI#139](https://github.com/cybersonic/LuCLI/issues/139)) | `server start --dry-run` no longer writes a default `lucee.json`; the preview shows `--name` | | 12 | wheels-dev/LuCLI [#24](https://github.com/wheels-dev/LuCLI/pull/24) (fork-only: follows row 2 / upstream PR [cybersonic/LuCLI#124](https://github.com/cybersonic/LuCLI/pull/124)) | Root options before a reserved root subcommand under an aliased binary run that subcommand instead of the module (`wheels --timing cfml 'x'`) | +| 13 | wheels-dev/LuCLI [#26](https://github.com/wheels-dev/LuCLI/pull/26) (issue: upstream [cybersonic/LuCLI#137](https://github.com/cybersonic/LuCLI/issues/137)) | `lucli run .cfm` runs the built-in variables setup inside `` instead of printing it | Upstream (row 8): issue [cybersonic/LuCLI#133](https://github.com/cybersonic/LuCLI/issues/133), PR [cybersonic/LuCLI#134](https://github.com/cybersonic/LuCLI/pull/134) (a `cherry-pick -x` of diff --git a/src/main/java/org/lucee/lucli/LuceeScriptEngine.java b/src/main/java/org/lucee/lucli/LuceeScriptEngine.java index 0c21b60..560efc7 100644 --- a/src/main/java/org/lucee/lucli/LuceeScriptEngine.java +++ b/src/main/java/org/lucee/lucli/LuceeScriptEngine.java @@ -660,12 +660,26 @@ private void executeTemplate(String scriptFile, String scriptContent, String[] s StringOutput.getInstance().println("${EMOJI_INFO} Arguments: " + Arrays.toString(scriptArgs)); } - // For CFS scripts, inject built-in variables directly into the script content - // so they are available as CFML variables, not just engine bindings - String scriptWithVariables = injectBuiltinVariables(scriptContent, scriptFile, scriptArgs); + // A .cfm is a template: text outside is output. The built-in + // variables setup is script, so it must run inside ; prepending it + // as plain text printed it before the template's output (upstream + // cybersonic/LuCLI#137). + String scriptWithVariables = scriptContent; + Timer.start("injectBuiltinVariables"); + try { + BuiltinVariableManager variableManager = BuiltinVariableManager.getInstance(isVerboseMode(), isDebugMode()); + scriptWithVariables = wrapBuiltinSetupForTemplate( + variableManager.createVariableSetupScript(scriptFile, scriptArgs), scriptContent); + } catch (IOException e) { + if (isDebugMode()) { + System.err.println("Warning: Failed to inject built-in variables: " + e.getMessage()); + } + } finally { + Timer.stop("injectBuiltinVariables"); + } if (isVerboseMode() || isDebugMode()) { - System.out.println("=== CFS Script with Built-in Variables ==="); + System.out.println("=== CFM Template with Built-in Variables ==="); System.out.println(scriptWithVariables); System.out.println("=== End Script ==="); } @@ -676,6 +690,18 @@ private void executeTemplate(String scriptFile, String scriptContent, String[] s } + /** + * Prefix a template (.cfm) with the built-in variables setup, run as script: + * {@code setup} then the template unchanged. No other text is + * added before the template, so its output is exactly what it renders. + */ + static String wrapBuiltinSetupForTemplate(String variableSetup, String templateContent) { + if (variableSetup == null || variableSetup.isBlank()) { + return templateContent; + } + return "\n" + variableSetup + "\n" + templateContent; + } + private void executeWrappedScript(String scriptContent, String scriptFile, String[] scriptArgs) throws Exception { // Try internal component loading first, fall back to filesystem extraction if needed diff --git a/src/test/java/org/lucee/lucli/TemplatePreludeTest.java b/src/test/java/org/lucee/lucli/TemplatePreludeTest.java new file mode 100644 index 0000000..976fb89 --- /dev/null +++ b/src/test/java/org/lucee/lucli/TemplatePreludeTest.java @@ -0,0 +1,34 @@ +package org.lucee.lucli; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +/** + * A .cfm run by `lucli run` gets the built-in variables setup inside , so the + * setup runs instead of being printed before the template's output + * (upstream cybersonic/LuCLI#137). + */ +class TemplatePreludeTest { + + @Test + void setupRunsInsideCfscriptAndTheTemplateFollowsUnchanged() { + String setup = "// LuCLI Built-in Variables Setup\n__scriptDir = '/tmp';"; + String template = "hello #1+1#"; + String wrapped = LuceeScriptEngine.wrapBuiltinSetupForTemplate(setup, template); + + assertTrue(wrapped.startsWith("\n" + setup + "\n")); + assertTrue(wrapped.endsWith(template)); + assertEquals(wrapped.indexOf("") + "".length(), wrapped.indexOf(template), + "nothing may sit between the setup and the template, or it is output"); + assertFalse(wrapped.contains("Original Script Content")); + } + + @Test + void emptySetupLeavesTheTemplateAsIs() { + assertEquals("

x

", LuceeScriptEngine.wrapBuiltinSetupForTemplate("", "

x

")); + assertEquals("

x

", LuceeScriptEngine.wrapBuiltinSetupForTemplate(null, "

x

")); + } +}