Skip to content
Open
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
11 changes: 7 additions & 4 deletions apps/signup-form/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,20 @@ module.exports = {
}
},
rules: {
// sort multiple import lines into alphabetical groups
// Sort multiple import lines into alphabetical groups
'ghost/sort-imports-es6-autofix/sort-imports-es6': ['error', {
memberSyntaxSortOrder: ['none', 'all', 'single', 'multiple']
}],

// suppress errors for missing 'import React' in JSX files, as we don't need it
// Enforce kebab-case (lowercase with hyphens) for all filenames
'ghost/filenames/match-regex': ['error', '^[a-z0-9.-]+$', false],

Comment on lines +18 to +25
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

fd package.json -type f | head -20

Repository: agentic-review-benchmarks/Ghost-combined-coderabbit

Length of output: 265


🏁 Script executed:

find . -name "package.json" -path "*/apps/signup-form/*" -o -name "package.json" -path "*/package.json" | head -5

Repository: agentic-review-benchmarks/Ghost-combined-coderabbit

Length of output: 269


🏁 Script executed:

cat -n package.json | head -50

Repository: agentic-review-benchmarks/Ghost-combined-coderabbit

Length of output: 3699


🌐 Web query:

eslint-plugin-ghost filenames/match-regex rule documentation

💡 Result:

filenames/match-regex is not a rule from eslint-plugin-ghost; it comes from eslint-plugin-filenames. eslint-plugin-ghost is a shared config/plugin bundle, but this specific rule is documented in eslint-plugin-filenames’ README. [3]

eslint-plugin-filenames: filenames/match-regex

What it does: Enforces a filename convention using a regular expression (default convention is camelCase.js). [1]

Rule config:

  • Option 1: pattern (string regex)
  • Option 2: ignoreExporting (boolean) — when true, files with a named default export (per the plugin’s export-detection logic) are ignored by this rule. [1]

Example (snake_case filenames, ignore exporting files):

{
  "plugins": ["filenames"],
  "rules": {
    "filenames/match-regex": [2, "^[a-z_]+$", true]
  }
}

This would flag camelCase.js and allow snake_case.js. [1]

If you’re on ESLint v9 / using eslint-plugin-github

GitHub notes that filenames/match-regex was adapted into eslint-plugin-github as:

  • github/filenames-match-regex (same idea, different rule name) [2]

Example:

'github/filenames-match-regex': ['error', '^([a-z0-9]+)([A-Z][a-z0-9]+)*$']

[2]

Sources: [1] [2] [3]


🌐 Web query:

eslint-plugin-ghost NPM package rules

💡 Result:

eslint-plugin-ghost (v3.4.4) ships these custom ESLint rules (rule IDs are typically ghost/<rule-name>):

  • ghost-error-usage
  • ghost-tpl-usage
  • max-api-complexity
  • max-api-lines
  • no-native-error
  • node-assert-methods
  • node-assert-params
  • node-assert-strict [1]

The package’s entrypoint is lib/index.js and the canonical repo is TryGhost/eslint-plugin-ghost. [2]

To use the plugin in general, Ghost’s README shows enabling plugins: ['ghost'] and extending one of its shared configs via plugin:ghost/<config>. [3]


Fix the namespace prefix for the filenames rule.

The rule ghost/filenames/match-regex will fail because it uses an incorrect prefix. The filenames/match-regex rule comes from eslint-plugin-filenames, not eslint-plugin-ghost. Change it to filenames/match-regex instead. The third argument (boolean false) is valid—it sets the ignoreExporting option—but won't matter if the rule isn't found.

🤖 Prompt for AI Agents
In `@apps/signup-form/.eslintrc.cjs` around lines 18 - 25, The ESLint rule is
using the wrong namespace: replace the incorrect rule key
"ghost/filenames/match-regex" with "filenames/match-regex" so ESLint finds the
rule from eslint-plugin-filenames; keep the existing options (the regex
'^[a-z0-9.-]+$' and the third boolean argument false which corresponds to
ignoreExporting).

// Suppress errors for missing 'import React' in JSX files, as we don't need it
'react/react-in-jsx-scope': 'off',
// ignore prop-types for now
// Ignore prop-types for now
'react/prop-types': 'off',

// custom react rules
// Custom react rules
'react/jsx-sort-props': ['error', {
reservedFirst: true,
callbacksLast: true,
Expand Down
2 changes: 1 addition & 1 deletion apps/signup-form/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import i18nLib from '@tryghost/i18n';

import type {Preview} from "@storybook/react";
import './storybook.css';
import {AppContextProvider, AppContextType} from '../src/AppContext';
import {AppContextProvider, AppContextType} from '../src/app-context';

const transparencyGrid = `url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3ERectangle%3C/title%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath fill='%23F2F6F8' d='M0 0h24v24H0z'/%3E%3Cpath fill='%23E5ECF0' d='M0 0h12v12H0zM12 12h12v12H12z'/%3E%3C/g%3E%3C/svg%3E")`

Expand Down
19 changes: 18 additions & 1 deletion apps/signup-form/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,28 @@ Follow the instructions for the top-level repo.
1. `git clone` this repo & `cd` into it as usual
2. Run `yarn` to install top-level dependencies.


## Test

- `yarn lint` run just eslint
- `yarn test` run lint and tests
- `yarn test:e2e` run e2e tests on Chromium
- `yarn test:slowmo` run e2e tests visually (headed) and slower on Chromium
- `yarn test:e2e:full` run e2e tests on all browsers

## Release

A patch release can be rolled out instantly in production, whereas a minor/major release requires the Ghost monorepo to be updated and released.
In either case, you need sufficient permissions to release `@tryghost` packages on NPM.

### Patch release

1. Run `yarn ship` and select a patch version when prompted
2. Merge the release commit to `main`

### Minor / major release

1. Run `yarn ship` and select a minor or major version when prompted
2. Merge the release commit to `main`
3. Wait until a new version of Ghost is released

To use the new version of signup form in Ghost, update the version in Ghost core's default configuration (currently at `core/shared/config/default.json`)
2 changes: 1 addition & 1 deletion apps/signup-form/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tryghost/signup-form",
"version": "0.3.2",
"version": "0.3.3",
"license": "MIT",
"repository": {
"type": "git",
Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions apps/signup-form/src/App.tsx → apps/signup-form/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, {ComponentProps} from 'react';
import i18nLib from '@tryghost/i18n';
import pages, {Page, PageName} from './pages';
import {AppContextProvider, AppContextType} from './AppContext';
import {ContentBox} from './components/ContentBox';
import {Frame} from './components/Frame';
import {AppContextProvider, AppContextType} from './app-context';
import {ContentBox} from './components/content-box';
import {Frame} from './components/frame';
import {setupGhostApi} from './utils/api';
import {useOptions} from './utils/options';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import IFrame from './IFrame';
import IFrame from './iframe';
import React, {useCallback, useState} from 'react';
import styles from '../styles/iframe.css?inline';
import {isMinimal} from '../utils/helpers';
import {useAppContext} from '../AppContext';
import {useAppContext} from '../app-context';

type FrameProps = {
children: React.ReactNode
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import {FormView} from './FormView';
import {FormView} from './form-view';
import {isMinimal} from '../../utils/helpers';
import {isValidEmail} from '../../utils/validator';
import {useAppContext} from '../../AppContext';
import {useAppContext} from '../../app-context';

export const FormPage: React.FC = () => {
const [error, setError] = React.useState('');
Expand All @@ -27,7 +27,6 @@ export const FormPage: React.FC = () => {
if (minimal) {
// Don't go to the success page, but show the success state in the form
setSuccess(true);
setLoading(false);
} else {
setPage('SuccessPage', {
email
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {Meta, StoryObj} from '@storybook/react';

import {FormView} from './FormView';
import {FormView} from './form-view';

const meta = {
title: 'Form View',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, {FormEventHandler} from 'react';
import {ReactComponent as LoadingIcon} from '../../../assets/icons/spinner.svg';
import {useAppContext} from '../../AppContext';
import {useAppContext} from '../../app-context';

export const FormView: React.FC<FormProps & {
isMinimal: boolean
Expand Down Expand Up @@ -53,7 +53,7 @@ const Form: React.FC<FormProps> = ({isMinimal, loading, success, error, buttonCo

const submitHandler: FormEventHandler<HTMLFormElement> = (e) => {
e.preventDefault();
onSubmit({email});
onSubmit({email: email.trim()});
};

// The complicated transitions are here so that we animate visibility: hidden (step-start/step-end), which is required for screen readers to know what is visible (they ignore opacity: 0)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import {SuccessView} from './SuccessView';
import {useAppContext} from '../../AppContext';
import {SuccessView} from './success-view';
import {useAppContext} from '../../app-context';

type SuccessPageProps = {
email: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {Meta, StoryObj} from '@storybook/react';

import {SuccessView} from './SuccessView';
import {SuccessView} from './success-view';

const meta = {
title: 'Success View',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import {ReactComponent as EmailIcon} from '../../../assets/icons/email.svg';
import {useAppContext} from '../../AppContext';
import {useAppContext} from '../../app-context';

export const SuccessView: React.FC<{
email: string;
Expand Down
2 changes: 1 addition & 1 deletion apps/signup-form/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import App from './App.tsx';
import App from './app.tsx';
import React from 'react';
import ReactDOM from 'react-dom/client';
import {ROOT_DIV_CLASS} from './utils/constants';
Expand Down
4 changes: 2 additions & 2 deletions apps/signup-form/src/pages.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import {FormPage} from './components/pages/FormPage';
import {SuccessPage} from './components/pages/SuccessPage';
import {FormPage} from './components/pages/form-page';
import {SuccessPage} from './components/pages/success-page';

const Pages = {
FormPage,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, {useState} from 'react';
import i18nLib from '@tryghost/i18n';
import pages, {Page, PageName} from './pages';
import {AppContextProvider, SignupFormOptions} from './AppContext';
import {ContentBox} from './components/ContentBox';
import {AppContextProvider, SignupFormOptions} from './app-context';
import {ContentBox} from './components/content-box';
import {userEvent, within} from '@storybook/testing-library';
import type {Meta, StoryObj} from '@storybook/react';

Expand Down
8 changes: 3 additions & 5 deletions apps/signup-form/src/utils/helpers.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {SignupFormOptions} from '../AppContext';
import {SignupFormOptions} from '../app-context';

export type URLHistory = {
type?: 'post',
Expand All @@ -17,7 +17,7 @@ export function isMinimal(options: SignupFormOptions): boolean {
* Get the URL history when the form is embedded on the site itself.
*/
export function getDefaultUrlHistory() {
const STORAGE_KEY = 'ghost-history';
const STORAGE_KEY = "ghost-history";

try {
const historyString = sessionStorage.getItem(STORAGE_KEY);
Expand All @@ -42,9 +42,7 @@ export function getUrlHistory({siteUrl}: {siteUrl: string}): URLHistory {
try {
if (window.location.host === new URL(siteUrl).host) {
const history = getDefaultUrlHistory();
if (history) {
return history;
}
return history;
}
} catch (error) {
// Most likely an invalid siteUrl
Expand Down
2 changes: 1 addition & 1 deletion apps/signup-form/src/utils/options.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import {SignupFormOptions} from '../AppContext';
import {SignupFormOptions} from '../app-context';

export function useOptions(scriptTag: HTMLElement) {
const buildOptions = React.useCallback(() => {
Expand Down
2 changes: 1 addition & 1 deletion ghost/core/core/shared/config/defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -302,5 +302,5 @@
"captureLinkClickBadMemberUuid": false
},
"disableJSBackups": false,
"memberWelcomeEmailTestInbox" : ""
"memberWelcomeEmailTestInbox": ""
}