diff --git a/README.md b/README.md index baf551d..b0e1986 100644 --- a/README.md +++ b/README.md @@ -486,6 +486,62 @@ The CLI detects BoxLang projects using three methods (in order of precedence): } ``` +### 🗂️ App Layout Detection + +The CLI automatically detects whether your project uses a **modern** or **flat** layout and generates files in the correct location. + +| Layout | Detection | App Code Location | Tests Location | +| ------ | --------- | ----------------- | -------------- | +| **Modern** | Both `app/` and `public/` directories exist | `/app/models`, `/app/handlers`, etc. | `/tests/` | +| **Flat** | Default (no `app/` + `public/`) | `/models`, `/handlers`, etc. | `/tests/` | + +#### Modern Layout (e.g. `boxlang`, `modern` templates) + +```text +/app - Application source code + /config - Configuration + /handlers - Event handlers + /models - Models & services + /views - View templates + /layouts - Layout wrappers + /interceptors - Interceptors +/public - Web root +/modules - ColdBox modules +/tests - Test suites +/resources - Migrations, seeders, etc. +``` + +#### Flat Layout (e.g. `flat`, legacy templates) + +```text +/config - Configuration +/handlers - Event handlers +/models - Models & services +/views - View templates +/layouts - Layout wrappers +/interceptors - Interceptors +/modules - ColdBox modules +/tests - Test suites +``` + +When you run a generation command in a modern-layout project, the CLI automatically targets the correct directory: + +```bash +# In a flat layout project → creates /handlers/users.cfc +# In a modern layout project → creates /app/handlers/users.cfc +coldbox create handler users + +# In a flat layout project → creates /models/User.cfc +# In a modern layout project → creates /app/models/User.cfc +coldbox create model User +``` + +You can always override the target directory explicitly: + +```bash +coldbox create model User directory=app/models +``` + #### 🚀 Usage Examples ```bash diff --git a/box.json b/box.json index c261dc5..288d083 100644 --- a/box.json +++ b/box.json @@ -1,6 +1,6 @@ { "name":"ColdBox CLI", - "version":"8.9.0", + "version":"8.10.0", "location":"https://downloads.ortussolutions.com/ortussolutions/commandbox-modules/coldbox-cli/@build.version@/coldbox-cli-@build.version@.zip", "slug":"coldbox-cli", "author":"Ortus Solutions, Corp", diff --git a/changelog.md b/changelog.md index 3ea0753..70771ba 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Automatic app layout detection for code generation commands. The CLI now detects whether the project uses a modern layout (with `app/` and `public/` directories) or a flat layout, and automatically places generated files in the correct location (e.g., `app/models` vs `models`, `app/handlers` vs `handlers`, etc.). + ## [8.9.0] - 2026-04-07 ### Updates diff --git a/commands/coldbox/create/handler.cfc b/commands/coldbox/create/handler.cfc index cc02347..299dd02 100644 --- a/commands/coldbox/create/handler.cfc +++ b/commands/coldbox/create/handler.cfc @@ -42,11 +42,11 @@ component aliases="coldbox create controller" extends="coldbox-cli.models.BaseCo required name, actions = "", boolean views = true, - viewsDirectory = "views", + viewsDirectory = getAppPrefix( getCWD() ) & "views", boolean integrationTests = true, appMapping = "/", testsDirectory = "tests/specs/integration", - directory = "handlers", + directory = getAppPrefix( getCWD() ) & "handlers", description = "I am a new handler", boolean open = false, boolean rest = false, diff --git a/commands/coldbox/create/interceptor.cfc b/commands/coldbox/create/interceptor.cfc index 5b187d5..e86119a 100644 --- a/commands/coldbox/create/interceptor.cfc +++ b/commands/coldbox/create/interceptor.cfc @@ -29,7 +29,7 @@ component extends="coldbox-cli.models.BaseCommand" { description = "I am a new interceptor", boolean tests = true, testsDirectory = "tests/specs/interceptors", - directory = "interceptors", + directory = getAppPrefix( getCWD() ) & "interceptors", boolean open = false, boolean force = false, boolean boxlang = isBoxLangProject( getCWD() ) diff --git a/commands/coldbox/create/layout.cfc b/commands/coldbox/create/layout.cfc index 904713c..1000bcb 100644 --- a/commands/coldbox/create/layout.cfc +++ b/commands/coldbox/create/layout.cfc @@ -21,7 +21,7 @@ component extends="coldbox-cli.models.BaseCommand" { function run( required name, boolean helper = false, - directory = "layouts", + directory = getAppPrefix( getCWD() ) & "layouts", boolean open = false, boolean force = false, content = "

#arguments.name# Layout

#variables.utility.BREAK#", diff --git a/commands/coldbox/create/model.cfc b/commands/coldbox/create/model.cfc index 5796487..554cca3 100644 --- a/commands/coldbox/create/model.cfc +++ b/commands/coldbox/create/model.cfc @@ -60,7 +60,7 @@ component extends="coldbox-cli.models.BaseCommand" { persistence = "transient", boolean tests = true, testsDirectory = "tests/specs/unit", - directory = "models", + directory = getAppPrefix( getCWD() ) & "models", description = "I am a new Model Object", boolean open = false, boolean accessors = true, diff --git a/commands/coldbox/create/orm-crud.cfc b/commands/coldbox/create/orm-crud.cfc index e8d43b9..1bbd0fe 100644 --- a/commands/coldbox/create/orm-crud.cfc +++ b/commands/coldbox/create/orm-crud.cfc @@ -22,8 +22,8 @@ component extends="coldbox-cli.models.BaseCommand" { function run( required entity, pluralName = "", - handlersDirectory = "handlers", - viewsDirectory = "views", + handlersDirectory = getAppPrefix( getCWD() ) & "handlers", + viewsDirectory = getAppPrefix( getCWD() ) & "views", boolean tests = true, testsDirectory = "tests/specs/integration", boolean boxlang = isBoxLangProject( getCWD() ) diff --git a/commands/coldbox/create/orm-entity.cfc b/commands/coldbox/create/orm-entity.cfc index f668d84..2c0f19d 100644 --- a/commands/coldbox/create/orm-entity.cfc +++ b/commands/coldbox/create/orm-entity.cfc @@ -56,7 +56,7 @@ component extends="coldbox-cli.models.BaseCommand" { function run( required entityName, table = "", - directory = "models", + directory = getAppPrefix( getCWD() ) & "models", boolean activeEntity = false, primaryKey = "id", primaryKeyColumn = "", diff --git a/commands/coldbox/create/orm-service.cfc b/commands/coldbox/create/orm-service.cfc index c4fa851..284b68f 100644 --- a/commands/coldbox/create/orm-service.cfc +++ b/commands/coldbox/create/orm-service.cfc @@ -23,7 +23,7 @@ component extends="coldbox-cli.models.BaseCommand" { **/ function run( required serviceName, - directory = "models", + directory = getAppPrefix( getCWD() ) & "models", boolean queryCaching = false, boolean eventHandling = true, cacheRegion = "", diff --git a/commands/coldbox/create/orm-virtual-service.cfc b/commands/coldbox/create/orm-virtual-service.cfc index 53788d7..349f9be 100644 --- a/commands/coldbox/create/orm-virtual-service.cfc +++ b/commands/coldbox/create/orm-virtual-service.cfc @@ -23,7 +23,7 @@ component extends="coldbox-cli.models.BaseCommand" { **/ function run( required entityName, - directory = "models", + directory = getAppPrefix( getCWD() ) & "models", boolean queryCaching = false, boolean eventHandling = true, cacheRegion = "", diff --git a/commands/coldbox/create/resource.cfc b/commands/coldbox/create/resource.cfc index cd3c472..0088832 100644 --- a/commands/coldbox/create/resource.cfc +++ b/commands/coldbox/create/resource.cfc @@ -91,9 +91,9 @@ component extends="coldbox-cli.models.BaseCommand" { generator = "native", properties = "", modulesDirectory = "modules_app", - handlersDirectory = "handlers", - viewsDirectory = "views", - modelsDirectory = "models", + handlersDirectory = getAppPrefix( getCWD() ) & "handlers", + viewsDirectory = getAppPrefix( getCWD() ) & "views", + modelsDirectory = getAppPrefix( getCWD() ) & "models", boolean tests = true, specsDirectory = "tests/specs", boolean api = false, diff --git a/commands/coldbox/create/service.cfc b/commands/coldbox/create/service.cfc index 8e77ef2..336ada9 100644 --- a/commands/coldbox/create/service.cfc +++ b/commands/coldbox/create/service.cfc @@ -33,7 +33,7 @@ component extends="coldbox-cli.models.BaseCommand" { methods = "", boolean tests = true, testsDirectory = "tests/specs/unit", - directory = "models", + directory = getAppPrefix( getCWD() ) & "models", description = "I am a new service", boolean open = false, boolean force = false, diff --git a/commands/coldbox/create/view.cfc b/commands/coldbox/create/view.cfc index 55210c9..dffebfc 100644 --- a/commands/coldbox/create/view.cfc +++ b/commands/coldbox/create/view.cfc @@ -22,7 +22,7 @@ component extends="coldbox-cli.models.BaseCommand" { function run( required name, boolean helper = false, - directory = "views", + directory = getAppPrefix( getCWD() ) & "views", boolean open = false, content = "

#arguments.name# view

", boolean force = false, diff --git a/models/BaseCommand.cfc b/models/BaseCommand.cfc index 7d48e46..1a6adcb 100644 --- a/models/BaseCommand.cfc +++ b/models/BaseCommand.cfc @@ -17,6 +17,19 @@ component accessors="true" { return this; } + /** + * Detects the application layout and returns the appropriate path prefix. + * In a modern layout (app/ and public/ directories exist), returns "app/". + * In a flat layout, returns "". + * + * @cwd The current working directory + * + * @return string "app/" for modern layout, "" for flat layout + */ + function getAppPrefix( required cwd ){ + return variables.utility.detectTemplateType( cwd ) == "modern" ? "app/" : ""; + } + /** * Determines if we are running on a BoxLang server * or using the BoxLang runner.