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
4 changes: 2 additions & 2 deletions packages/kg-converters/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
cjs/
es/
build/
tsconfig.tsbuildinfo
70 changes: 29 additions & 41 deletions packages/kg-converters/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,45 +1,33 @@
import {fixupPluginRules} from '@eslint/compat';
import {defineConfig} from 'eslint/config';
import eslint from '@eslint/js';
import ghostPlugin from 'eslint-plugin-ghost';
import globals from 'globals';
import tseslint from 'typescript-eslint';

const ghost = fixupPluginRules(ghostPlugin);

export default [
{ignores: ['build/**', 'cjs/**', 'es/**']},
eslint.configs.recommended,
{
files: ['**/*.js'],
plugins: {ghost},
languageOptions: {
globals: {
...globals.node,
...globals.browser
}
},
rules: {
...ghostPlugin.configs.node.rules,
// match ESLint 8 behavior for catch clause variables
'no-unused-vars': ['error', {caughtErrors: 'none'}],
// disable rules incompatible with ESLint 9 flat config
'ghost/filenames/match-exported-class': 'off',
'ghost/filenames/match-exported': 'off',
'ghost/filenames/match-regex': 'off'
}
export default defineConfig([
{ ignores: ['build/**'] },
{
files: ['**/*.ts'],
extends: [
eslint.configs.recommended,
tseslint.configs.recommended,
],
languageOptions: {
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
},
plugins: { ghost: ghostPlugin },
rules: {
...ghostPlugin.configs.ts.rules,
'@typescript-eslint/no-explicit-any': 'error',
Comment on lines +19 to +20
Copy link

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:

#!/bin/bash
set -euo pipefail

plugin_dir="$(fd '^eslint-plugin-ghost$' packages -t d | head -n1)"
if [ -z "$plugin_dir" ]; then
  echo "Could not locate eslint-plugin-ghost under packages/" >&2
  exit 1
fi

rg -n -C8 "\\bts\\b\\s*:\\s*\\{|['\\\"]ts-test['\\\"]\\s*:\\s*\\{|languageOptions\\s*:|plugins\\s*:|settings\\s*:|ignores\\s*:" "$plugin_dir" -g '!**/build/**'

Repository: TryGhost/Koenig

Length of output: 111


🏁 Script executed:

#!/bin/bash

# Check if eslint-plugin-ghost is listed as an external dependency
echo "=== Checking package.json for eslint-plugin-ghost ==="
fd package.json packages/kg-converters -x cat {} | grep -A10 -B10 "eslint-plugin-ghost" || echo "Not found in direct search"

# Display the full eslint.config.mjs file to see the imports and context
echo -e "\n=== Full eslint.config.mjs content ==="
cat packages/kg-converters/eslint.config.mjs

# Check if there's a monorepo root package.json
echo -e "\n=== Checking root package.json for eslint-plugin-ghost ==="
cat package.json | grep -A5 -B5 "eslint-plugin-ghost" || echo "Not found in root"

Repository: TryGhost/Koenig

Length of output: 1368


🌐 Web query:

eslint-plugin-ghost npm preset config ts rules languageOptions

💡 Result:

eslint-plugin-ghost ships legacy .eslintrc* “preset configs” (exposed as plugin:ghost/<preset>), not ESLint v9 flat-config languageOptions objects. Usage is via extends: ['plugin:ghost/<preset>'] (presets include node, browser, es, ember, ts, test, filenames, plus *-no-style variants, and ts-test). [1]

What the ts preset does (rules / parser options)

The plugin:ghost/ts preset:

  • sets env: { node: true, es6: true }
  • sets parserOptions: { ecmaVersion: 2022 }
  • enables @typescript-eslint and extends plugin:@typescript-eslint/recommended``
  • merges Ghost’s shared rules and disables @typescript-eslint/no-inferrable-types [2]

plugin:ghost/ts-test extends ./ts and adds ./test-rules. [3]

ESLint v9 flat config (languageOptions) equivalent (if you want to avoid .eslintrc)

If you’re using eslint.config.js, you typically bring these legacy presets in via FlatCompat, then add/override languageOptions as needed (especially the TS parser):

// eslint.config.js
import { FlatCompat } from '@eslint/eslintrc'
import tsParser from '@typescript-eslint/parser'

const compat = new FlatCompat({ baseDirectory: import.meta.dirname })

export default [
  ...compat.extends('plugin:ghost/ts'),
  {
    files: ['**/*.{ts,tsx}'],
    languageOptions: {
      parser: tsParser,
      parserOptions: { ecmaVersion: 2022 },
    },
  },
]

(Typescript-eslint’s flat-config docs show languageOptions.parser / languageOptions.parserOptions as the place to configure TS parsing in flat config.) [4]

Sources: [1] [2] [3] [4]


Replace spread of Ghost plugin rules with proper ESLint v9 flat-config integration.

eslint-plugin-ghost v3.5.0 exports legacy .eslintrc* presets (e.g., plugin:ghost/ts, plugin:ghost/ts-test), not ESLint v9 flat-config objects. Accessing ghostPlugin.configs.ts.rules and ghostPlugin.configs['ts-test'].rules is not compatible with the plugin's actual structure and will not work reliably.

Use @eslint/eslintrc's FlatCompat to bridge the legacy presets into flat config, or migrate to .eslintrc.json format. If using FlatCompat:

♻️ Example using FlatCompat
import { FlatCompat } from '@eslint/eslintrc';
import tseslint from 'typescript-eslint';
import path from 'path';
import { fileURLToPath } from 'url';

const dirname = path.dirname(fileURLToPath(import.meta.url));
const compat = new FlatCompat({ baseDirectory: dirname });

export default tseslint.config([
  { ignores: ['build/**'] },
  ...compat.extends('plugin:ghost/ts'),
  {
    files: ['**/*.ts'],
    rules: {
      '@typescript-eslint/no-explicit-any': 'error',
    },
  },
  {
    files: ['test/**/*.ts'],
    ...compat.extends('plugin:ghost/ts-test'),
    rules: {
      'ghost/mocha/no-global-tests': 'off',
      'ghost/mocha/handle-done-callback': 'off',
      'ghost/mocha/no-mocha-arrows': 'off',
      'ghost/mocha/max-top-level-suites': 'off',
    },
  },
]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/kg-converters/eslint.config.mjs` around lines 18 - 19, The current
config spreads ghostPlugin.configs.ts.rules (and similarly ts-test) which are
legacy .eslintrc presets and incompatible with ESLint v9 flat-config; replace
that spread usage by converting the legacy presets via `@eslint/eslintrc`'s
FlatCompat (create a FlatCompat instance, call compat.extends('plugin:ghost/ts')
and compat.extends('plugin:ghost/ts-test') where needed) and then supply the
returned arrays into the tseslint.config call alongside your files/ rules blocks
(leave your '@typescript-eslint/no-explicit-any' rule in the appropriate
files-scoped rules object); ensure you import and instantiate FlatCompat with a
correct baseDirectory (use fileURLToPath(dirname) pattern) so the legacy plugin
presets are bridged into the flat config.

},
},
{
files: ['test/**/*.ts'],
rules: {
...ghostPlugin.configs['ts-test'].rules,
'ghost/mocha/no-global-tests': 'off',
'ghost/mocha/handle-done-callback': 'off',
'ghost/mocha/no-mocha-arrows': 'off',
'ghost/mocha/max-top-level-suites': 'off',
},
{
files: ['test/**/*.js'],
plugins: {ghost},
languageOptions: {
globals: {
...globals.node,
...globals.mocha,
should: true,
sinon: true
}
},
rules: {
...ghostPlugin.configs.test.rules
}
}
];
},
]);
1 change: 0 additions & 1 deletion packages/kg-converters/index.js

This file was deleted.

4 changes: 0 additions & 4 deletions packages/kg-converters/lib/kg-converters.js

This file was deleted.

39 changes: 23 additions & 16 deletions packages/kg-converters/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,42 @@
"repository": "https://github.com/TryGhost/Koenig/tree/main/packages/kg-converters",
"author": "Ghost Foundation",
"license": "MIT",
"main": "cjs/kg-converters.js",
"module": "es/kg-converters.js",
"source": "lib/kg-converters.js",
"main": "build/cjs/index.js",
"module": "build/esm/index.js",
"types": "build/esm/index.d.ts",
"exports": {
".": {
"types": "./build/esm/index.d.ts",
"import": "./build/esm/index.js",
"require": "./build/cjs/index.js"
}
},
"scripts": {
"dev": "rollup -c -w",
"build": "rollup -c",
"prepare": "NODE_ENV=production yarn build",
"pretest": "yarn build",
"test:unit": "NODE_ENV=testing c8 --all --src lib/ --exclude lib/kg-converters.js --check-coverage --reporter text --reporter cobertura mocha './test/**/*.test.js'",
"dev": "tsc --watch --preserveWatchOutput",
"build": "tsc && tsc -p tsconfig.cjs.json && echo '{\"type\":\"module\"}' > build/esm/package.json",
"prepare": "tsc && tsc -p tsconfig.cjs.json && echo '{\"type\":\"module\"}' > build/esm/package.json",
"pretest": "tsc && tsc -p tsconfig.cjs.json && echo '{\"type\":\"module\"}' > build/esm/package.json && tsc -p tsconfig.test.json",
"test:unit": "NODE_ENV=testing c8 --all --src src/ --check-coverage --reporter text --reporter cobertura tsx node_modules/.bin/mocha './test/**/*.test.ts'",
Comment on lines +19 to +22
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In npm scripts, is echo '{"type":"module"}' > file with single quotes portable across POSIX shells and Windows cmd/PowerShell?

💡 Result:

No.

  • POSIX shells (/bin/sh, bash, dash, etc.): echo '{"type":"module"}' > file is fine—single quotes are standard shell quoting. npm runs scripts via /bin/sh by default on Unix-like systems. [1]
  • Windows (default npm behavior): npm runs scripts via cmd.exe by default (even if you launch npm run from PowerShell), and cmd.exe does not treat '...' as quoting—single quotes are just literal characters. So you’ll typically end up writing the quotes into the file (or otherwise get different parsing than you expect). [1][2]
  • PowerShell: PowerShell does support single-quoted strings, so the command itself would work if npm were using PowerShell as the script shell—but by default it isn’t. You can change the shell with npm’s script-shell config. [1][3]

If you need cross-platform behavior, prefer a Node-based write (e.g., node -e "require('fs').writeFileSync('file','{\"type\":\"module\"}\\n')"), or explicitly set script-shell to pwsh / bash for your project/CI. [3]

Sources
[1] npm Docs — npm run-script: default shell is /bin/sh on Unix, cmd.exe on Windows https://docs.npmjs.com/cli/v10/commands/npm-run-script/
[2] Stack Overflow — cmd.exe does not recognize single quotes as string delimiters https://stackoverflow.com/questions/562038/escaping-double-quotes-in-batch-script
[3] npm Docs — script-shell config (default /bin/sh on POSIX, cmd.exe on Windows) https://docs.npmjs.com/cli/v10/using-npm/config/


🏁 Script executed:

# Verify the file exists and check its content
if [ -f "packages/kg-converters/package.json" ]; then
  echo "=== File content (full) ==="
  cat -n "packages/kg-converters/package.json"
  echo ""
  echo "=== Lines 15-30 (context around lines 20-23) ==="
  sed -n '15,30p' "packages/kg-converters/package.json"
else
  echo "File not found at packages/kg-converters/package.json"
  # Try to find it
  find . -name "package.json" -path "*/kg-converters/*" 2>/dev/null | head -5
fi

Repository: TryGhost/Koenig

Length of output: 2723


Fix shell portability and eliminate build script duplication.

The echo '{"type":"module"}' > build/esm/package.json pattern is not portable to Windows (cmd.exe does not recognize single quotes as string delimiters), and the same 71-character build command is duplicated across build, prepare, and pretest.

Proposed fix
   "scripts": {
     "dev": "tsc --watch --preserveWatchOutput",
-    "build": "tsc && tsc -p tsconfig.cjs.json && echo '{\"type\":\"module\"}' > build/esm/package.json",
-    "prepare": "tsc && tsc -p tsconfig.cjs.json && echo '{\"type\":\"module\"}' > build/esm/package.json",
-    "pretest": "tsc && tsc -p tsconfig.cjs.json && echo '{\"type\":\"module\"}' > build/esm/package.json",
+    "write:esm-package-json": "node -e \"require('node:fs').writeFileSync('build/esm/package.json', JSON.stringify({type:'module'}))\"",
+    "build": "tsc && tsc -p tsconfig.cjs.json && yarn write:esm-package-json",
+    "prepare": "yarn build",
+    "pretest": "yarn build",
     "test:unit": "NODE_ENV=testing c8 --all --src src/ --check-coverage --reporter text --reporter cobertura tsx node_modules/.bin/mocha './test/**/*.test.ts'",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"build": "tsc && tsc -p tsconfig.cjs.json && echo '{\"type\":\"module\"}' > build/esm/package.json",
"prepare": "tsc && tsc -p tsconfig.cjs.json && echo '{\"type\":\"module\"}' > build/esm/package.json",
"pretest": "tsc && tsc -p tsconfig.cjs.json && echo '{\"type\":\"module\"}' > build/esm/package.json",
"test:unit": "NODE_ENV=testing c8 --all --src src/ --check-coverage --reporter text --reporter cobertura tsx node_modules/.bin/mocha './test/**/*.test.ts'",
"dev": "tsc --watch --preserveWatchOutput",
"write:esm-package-json": "node -e \"require('node:fs').writeFileSync('build/esm/package.json', JSON.stringify({type:'module'}))\"",
"build": "tsc && tsc -p tsconfig.cjs.json && yarn write:esm-package-json",
"prepare": "yarn build",
"pretest": "yarn build",
"test:unit": "NODE_ENV=testing c8 --all --src src/ --check-coverage --reporter text --reporter cobertura tsx node_modules/.bin/mocha './test/**/*.test.ts'",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/kg-converters/package.json` around lines 20 - 23, Replace the
duplicated, non-portable echo call with a single cross-platform npm script and
reference it from build, prepare, and pretest: add a new script (e.g.,
"write-esm-package") that uses Node (fs.mkdirSync with {recursive:true} and
fs.writeFileSync) to create build/esm and write {"type":"module"} to
build/esm/package.json, then simplify "build", "prepare", and "pretest" to run
their tsc commands and invoke the new "write-esm-package" script instead of
repeating the echo snippet.

"test": "yarn test:unit",
"posttest": "yarn lint",
"lint": "eslint . --cache"
},
"files": [
"LICENSE",
"README.md",
"cjs/",
"es/",
"lib"
"build"
],
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@rollup/plugin-babel": "7.0.0",
"@eslint/js": "9.39.4",
"@types/mocha": "^10.0.0",
"@types/node": "^22.0.0",
"c8": "11.0.0",
"mocha": "11.7.5",
"rollup": "4.59.0",
"sinon": "21.0.2"
"sinon": "21.0.2",
"tsx": "^4.0.0",
"typescript": "5.9.3",
"typescript-eslint": "8.33.1"
},
"dependencies": {
"lodash": "^4.17.21"
Expand Down
47 changes: 0 additions & 47 deletions packages/kg-converters/rollup.config.mjs

This file was deleted.

1 change: 1 addition & 0 deletions packages/kg-converters/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {lexicalToMobiledoc, mobiledocToLexical} from './kg-converters.js';
4 changes: 4 additions & 0 deletions packages/kg-converters/src/kg-converters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import {lexicalToMobiledoc} from './lexical-to-mobiledoc.js';
import {mobiledocToLexical} from './mobiledoc-to-lexical.js';

export {lexicalToMobiledoc, mobiledocToLexical};
Loading
Loading