Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,17 @@ This feature is especially helpful when:
The `--html-folder` option enables serving HTML files without extensions, useful for previewing content changes when you don't have access to the authoring system.

```
$ aem up --html-folder content
$ aem up --html-folder drafts # serves at /drafts/*
$ aem up --html-folder content --html-mount / # serves at /* (root)
$ aem up --html-folder drafts --html-mount /preview # serves at /preview/*
```

Use `--html-mount` to control the URL path where files are served. Without it, files are served at `/FOLDER/*`.

This enables two features:

1. **Extension-less URLs**: Access `/content/page` to serve `content/page.html`
2. **Plain HTML with metadata**: Create `content/page.plain.html` files that are automatically wrapped with proper HTML structure and metadata processing
1. **Extension-less URLs**: Access `/drafts/page` (or `/page` with root mount) to serve the corresponding `.html` file
2. **Plain HTML with metadata**: Create `.plain.html` files that are automatically wrapped with proper HTML structure and metadata processing

#### Plain HTML files (.plain.html)

Expand Down Expand Up @@ -181,7 +185,8 @@ If present, `ALL_PROXY` is used as fallback if there is no other match.
| `--livereload` | `AEM_LIVERELOAD` | `true` | Enable automatic reloading of modified sources in browser. |
| `--no-livereload` | `AEM_NO_LIVERELOAD` | `false` | Disable live-reload. |
| `--forward-browser-logs` | `AEM_FORWARD_BROWSER_LOGS` | `false` | Forward browser console logs to terminal. |
| `--html-folder` | `AEM_HTML_FOLDER` | undefined | Serve HTML files from folder without extensions. Supports .html and .plain.html files. |
| `--html-folder` | `AEM_HTML_FOLDER` | undefined | Serve HTML files from folder without extensions. |
| `--html-mount` | `AEM_HTML_MOUNT` | `/FOLDER` | URL path where html-folder files are served. |
| `--open` | `AEM_OPEN` | `/` | Open a browser window at specified path after server start. |
| `--no-open` | `AEM_NO_OPEN` | `false` | Disable automatic opening of browser window. |
| `--tls-key` | `AEM_TLS_KEY` | undefined | Path to .key file (for enabling TLS) |
Expand Down
16 changes: 10 additions & 6 deletions src/server/HelixProject.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,16 @@ export class HelixProject extends BaseProject {
if (path.isAbsolute(value) || value.includes('..') || value.startsWith('/')) {
throw new Error(`Invalid HTML folder name: ${value} only folders within the current workspace are allowed`);
}

this._htmlFolder = value;
this._server.withHtmlFolder(value);
} else {
this._htmlFolder = value;
this._server.withHtmlFolder(value);
}
return this;
}

withHtmlMount(value) {
this._htmlMount = value;
return this;
}

get proxyUrl() {
return this._proxyUrl;
}
Expand Down Expand Up @@ -142,6 +142,10 @@ export class HelixProject extends BaseProject {
}

async init() {
if (this._htmlFolder) {
const mount = this._htmlMount || `/${this._htmlFolder}`;
this._server.withHtmlFolder(this._htmlFolder, mount);
}
await super.init();
this._indexer = new Indexer()
.withLogger(this._logger)
Expand Down Expand Up @@ -206,7 +210,7 @@ export class HelixProject extends BaseProject {
await lstat(htmlFolderPath);
this.log.debug(`Registered HTML folder for live-reload: ${this._htmlFolder}`);
// Watch all HTML files in the folder - only .html extension
this.liveReload.registerFiles([`${htmlFolderPath}/**/*.html`], `/${this._htmlFolder}/`);
this.liveReload.registerFiles([`${htmlFolderPath}/**/*.html`], this._server.mountPrefix);
} catch (e) {
this.log.error(`HTML folder '${this._htmlFolder}' does not exist`);
throw new Error(`HTML folder '${this._htmlFolder}' does not exist`);
Expand Down
33 changes: 15 additions & 18 deletions src/server/HelixServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,17 @@ export class HelixServer extends BaseServer {
return this;
}

withHtmlFolder(value) {
// It's now sanitized in HelixProject.withHtmlFolder
this._htmlFolder = value;
withHtmlFolder(folder, mount) {
this._htmlFolder = folder;
this._htmlMount = mount;
this._mountPrefix = mount.endsWith('/') ? mount : `${mount}/`;
return this;
}

get mountPrefix() {
return this._mountPrefix;
}

async handleLogin(req, res) {
// disable autologin if login was called at least once
this._autoLogin = false;
Expand Down Expand Up @@ -245,22 +250,15 @@ export class HelixServer extends BaseServer {
* @param {Function} next next middleware
*/
async handleHtmlFolderRequest(req, res, next) {
if (!this._htmlFolder) {
return next();
}

// Use Express's req.path for pathname extraction
const pathname = req.path;
const folderPrefix = `/${this._htmlFolder}/`;

// Check if the request is for the HTML folder
if (!pathname.startsWith(folderPrefix)) {
let relativePath;
if (pathname.startsWith(this._mountPrefix)) {
relativePath = pathname.slice(this._mountPrefix.length);
} else {
return next();
}

// Extract the path within the HTML folder
let relativePath = pathname.slice(folderPrefix.length);

// Handle directory requests (trailing slash) by appending 'index'
if (relativePath === '' || relativePath.endsWith('/')) {
relativePath = `${relativePath}index`.replace(/\/+/g, '/');
Expand Down Expand Up @@ -435,10 +433,9 @@ export class HelixServer extends BaseServer {

// Add HTML folder handler before the general proxy handler
if (this._htmlFolder) {
// Only handle GET requests for the HTML folder path
const htmlFolderPattern = new RegExp(`^/${this._htmlFolder}/.*`);
this.app.get(htmlFolderPattern, asyncHandler(this.handleHtmlFolderRequest.bind(this)));
this.log.info(`Serving HTML files from folder: ${this._htmlFolder}`);
const mountPattern = new RegExp(`^${this._mountPrefix}.*`);
this.app.get(mountPattern, asyncHandler(this.handleHtmlFolderRequest.bind(this)));
this.log.info(`Serving HTML files from folder: ${this._htmlFolder} at ${this._htmlMount}`);
}

const handler = asyncHandler(this.handleProxyModeRequest.bind(this));
Expand Down
9 changes: 7 additions & 2 deletions src/up.cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,15 @@ export default class UpCommand extends AbstractServerCommand {
}

withHtmlFolder(value) {
// Basic validation - detailed validation done in HelixProject
this._htmlFolder = value;
return this;
}

withHtmlMount(value) {
this._htmlMount = value;
return this;
}

async doStop() {
await super.doStop();
if (this._watcher) {
Expand Down Expand Up @@ -113,7 +117,8 @@ export default class UpCommand extends AbstractServerCommand {
.withAllowInsecure(this._allowInsecure)
.withSiteToken(this._siteToken)
.withCookies(this._cookies)
.withHtmlFolder(this._htmlFolder);
.withHtmlFolder(this._htmlFolder)
.withHtmlMount(this._htmlMount);

this.log.info(chalk`{yellow ___ ________ ___ __ __ v${pkgJson.version}}`);
this.log.info(chalk`{yellow / | / ____/ |/ / _____(_)___ ___ __ __/ /___ _/ /_____ _____}`);
Expand Down
8 changes: 7 additions & 1 deletion src/up.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,12 @@ export default function up() {
})
.option('html-folder', {
alias: 'htmlFolder',
describe: 'Serve HTML files from this folder without extensions (e.g., /folder/file serves folder/file.html or folder/file.plain.html) use this to preview content changes if you do not have access to the authoring system',
describe: 'Serve HTML files from this folder without extensions. Defaults to serving at /FOLDER.',
type: 'string',
})
.option('html-mount', {
alias: 'htmlMount',
describe: 'URL path where html-folder files are served (e.g., / for root). Defaults to /FOLDER.',
type: 'string',
})

Expand Down Expand Up @@ -147,6 +152,7 @@ export default function up() {
.withCache(argv.alphaCache)
.withCookies(argv.cookies)
.withHtmlFolder(argv.htmlFolder)
.withHtmlMount(argv.htmlMount)
.run();
},
};
Expand Down
Loading
Loading