Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions changelog.d/plugins-dir-optional.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- The legacy `plugins/` directory is now optional. The scaffold's `Application.cfc` jar-scan and the framework plugin loader (`Plugins.cfc` `$folders()`/`$files()`) now guard their directory listing with `DirectoryExists`, so an app that has removed `plugins/` (the common case now that packages live in `vendor/<name>/`) no longer errors at startup on engines whose directory listing throws on a missing path — Lucee/Adobe tolerate a missing dir, but stricter engines (e.g. RustCFML) did not. The plugins-directory lookup is deprecated and slated for removal in the next major
13 changes: 7 additions & 6 deletions cli/lucli/templates/app/public/Application.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ component output="false" {
this.sessionManagement = true;

// If a plugin has a jar or class file, automatically add the mapping to this.javasettings.
// Legacy plugins system (DEPRECATED — superseded by vendor/<name>/ packages).
// Only scan when a plugins/ directory exists, so a removed plugins/ dir does not
// error on engines whose directoryList() throws on a missing path (e.g. RustCFML;
// Lucee tolerates it). This lookup is slated for removal in the next major.
this.wheels.pluginDir = this.appDir & "../plugins";
this.wheels.pluginFolders = DirectoryList(
this.wheels.pluginDir,
"true",
"path",
"*.class|*.jar|*.java"
);
this.wheels.pluginFolders = DirectoryExists(this.wheels.pluginDir)
? DirectoryList(this.wheels.pluginDir, "true", "path", "*.class|*.jar|*.java")
: [];

for (this.wheels.folder in this.wheels.pluginFolders) {
if (!StructKeyExists(this, "javaSettings")) {
Expand Down
13 changes: 7 additions & 6 deletions examples/starter-app/public/Application.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ component output="false" {
this.sessionManagement = true;

// If a plugin has a jar or class file, automatically add the mapping to this.javasettings.
// Legacy plugins system (DEPRECATED — superseded by vendor/<name>/ packages).
// Only scan when a plugins/ directory exists, so a removed plugins/ dir does not
// error on engines whose directoryList() throws on a missing path (e.g. RustCFML;
// Lucee tolerates it). This lookup is slated for removal in the next major.
this.wheels.pluginDir = this.appDir & "../plugins";
this.wheels.pluginFolders = DirectoryList(
this.wheels.pluginDir,
"true",
"path",
"*.class|*.jar|*.java"
);
this.wheels.pluginFolders = DirectoryExists(this.wheels.pluginDir)
? DirectoryList(this.wheels.pluginDir, "true", "path", "*.class|*.jar|*.java")
: [];

for (this.wheels.folder in this.wheels.pluginFolders) {
if (!StructKeyExists(this, "javaSettings")) {
Expand Down
13 changes: 7 additions & 6 deletions examples/tweet/public/Application.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ component output="false" {
this.sessionManagement = true;

// If a plugin has a jar or class file, automatically add the mapping to this.javasettings.
// Legacy plugins system (DEPRECATED — superseded by vendor/<name>/ packages).
// Only scan when a plugins/ directory exists, so a removed plugins/ dir does not
// error on engines whose directoryList() throws on a missing path (e.g. RustCFML;
// Lucee tolerates it). This lookup is slated for removal in the next major.
this.wheels.pluginDir = this.appDir & "../plugins";
this.wheels.pluginFolders = DirectoryList(
this.wheels.pluginDir,
"true",
"path",
"*.class|*.jar|*.java"
);
this.wheels.pluginFolders = DirectoryExists(this.wheels.pluginDir)
? DirectoryList(this.wheels.pluginDir, "true", "path", "*.class|*.jar|*.java")
: [];

for (this.wheels.folder in this.wheels.pluginFolders) {
if (!StructKeyExists(this, "javaSettings")) {
Expand Down
13 changes: 7 additions & 6 deletions public/Application.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@ component output="false" {
this.sessionManagement = true;

// If a plugin has a jar or class file, automatically add the mapping to this.javasettings.
// Legacy plugins system (DEPRECATED — superseded by vendor/<name>/ packages).
// Only scan when a plugins/ directory exists, so a removed plugins/ dir does not
// error on engines whose directoryList() throws on a missing path (e.g. RustCFML;
// Lucee tolerates it). This lookup is slated for removal in the next major.
this.wheels.pluginDir = this.appDir & "../plugins";
this.wheels.pluginFolders = DirectoryList(
this.wheels.pluginDir,
"true",
"path",
"*.class|*.jar|*.java"
);
this.wheels.pluginFolders = DirectoryExists(this.wheels.pluginDir)
? DirectoryList(this.wheels.pluginDir, "true", "path", "*.class|*.jar|*.java")
: [];

for (this.wheels.folder in this.wheels.pluginFolders) {
if (!StructKeyExists(this, "javaSettings")) {
Expand Down
12 changes: 12 additions & 0 deletions vendor/wheels/Plugins.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,14 @@ component output="false" extends="wheels.Global"{
}

public query function $folders() {
// The legacy plugins/ directory is deprecated (superseded by vendor/<name>/
// packages) and may be absent. Skip the scan when it does not exist so
// engines whose directory listing throws on a missing path (e.g. RustCFML)
// don't fail at boot; Lucee/Adobe return empty for a missing dir anyway.
// Slated for removal with the plugins system in the next major.
if (!DirectoryExists(variables.$class.pluginPathFull)) {
return QueryNew("name,directory,type");
}
local.query = $directory(
action = "list",
directory = variables.$class.pluginPathFull,
Expand Down Expand Up @@ -1086,6 +1094,10 @@ component output="false" extends="wheels.Global"{
}

public query function $files() {
// See $folders(): the deprecated plugins/ directory may be absent.
if (!DirectoryExists(variables.$class.pluginPathFull)) {
return QueryNew("name,directory,type");
}
local.query = $directory(
action = "list",
directory = variables.$class.pluginPathFull,
Expand Down
55 changes: 55 additions & 0 deletions vendor/wheels/tests/specs/pluginsMissingDirSpec.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
component extends="wheels.WheelsTest" {

function run() {

g = application.wo

// The legacy plugins/ directory is deprecated (superseded by vendor/<name>/
// packages) and apps are expected to remove it. The plugin loader must not
// error when it is absent — Lucee/Adobe return empty for a missing dir, but
// stricter engines (e.g. RustCFML) throw on directory listing of a missing
// path, which previously failed onApplicationStart. $folders()/$files() now
// short-circuit to an empty query when the directory does not exist.
describe("plugin loader with an absent plugins/ directory", () => {

missingPath = "/wheels/tests/_assets/plugins/__this_directory_does_not_exist__"

it("initializes without throwing when the plugins directory is missing", () => {
var config = {
path = "wheels",
fileName = "Plugins",
method = "$init",
pluginPath = missingPath,
deletePluginDirectories = false,
overwritePlugins = false,
loadIncompatiblePlugins = true
}
var state = {thrown = false}
try {
pluginObj = $pluginObj(config)
} catch (any e) {
state.thrown = true
}
expect(state.thrown).toBeFalse()
})

it("$folders() returns an empty query for a missing directory", () => {
var config = {
path = "wheels", fileName = "Plugins", method = "$init", pluginPath = missingPath,
deletePluginDirectories = false, overwritePlugins = false, loadIncompatiblePlugins = true
}
var pluginObj = $pluginObj(config)
expect(pluginObj.$folders().recordCount).toBe(0)
})

it("$files() returns an empty query for a missing directory", () => {
var config = {
path = "wheels", fileName = "Plugins", method = "$init", pluginPath = missingPath,
deletePluginDirectories = false, overwritePlugins = false, loadIncompatiblePlugins = true
}
var pluginObj = $pluginObj(config)
expect(pluginObj.$files().recordCount).toBe(0)
})
})
}
}
Loading