Browse Source

Refactor code structure for improved readability and maintainability

Yuki Takei 1 week ago
parent
commit
aff038c361

+ 23 - 0
.kiro/specs/upgrade-fixed-packages/research.md

@@ -158,3 +158,26 @@
 - [TC39 RegExp.escape() Stage 4](https://socket.dev/blog/tc39-advances-3-proposals-to-stage-4-regexp-escaping-float16array-and-redeclarable-global-eval) — ES2026
 - [TC39 RegExp.escape() Stage 4](https://socket.dev/blog/tc39-advances-3-proposals-to-stage-4-regexp-escaping-float16array-and-redeclarable-global-eval) — ES2026
 - [Node.js require(esm) stability](https://joyeecheung.github.io/blog/2025/12/30/require-esm-in-node-js-from-experiment-to-stability/) — stable since Node.js 22.12.0
 - [Node.js require(esm) stability](https://joyeecheung.github.io/blog/2025/12/30/require-esm-in-node-js-from-experiment-to-stability/) — stable since Node.js 22.12.0
 - [Handsontable license change](https://handsontable.com/docs/javascript-data-grid/software-license/) — proprietary since v7.0.0
 - [Handsontable license change](https://handsontable.com/docs/javascript-data-grid/software-license/) — proprietary since v7.0.0
+
+## Final Audit Summary (2026-03-23)
+
+| Package | Previous Version | New Version | Action | Rationale |
+|---------|-----------------|-------------|--------|-----------|
+| `@aws-sdk/client-s3` | `3.454.0` | `^3.1014.0` | Upgraded | Pinning comment was misleading; S3 client is independent of mongodb constraint |
+| `@aws-sdk/s3-request-presigner` | `3.454.0` | `^3.1014.0` | Upgraded | Same as above |
+| `bootstrap` | `=5.3.2` | `^5.3.8` | Upgraded | Bug #39798 fixed in v5.3.4; SCSS compilation verified |
+| `escape-string-regexp` | `^4.0.0` | Removed | Replaced | Native `RegExp.escape()` (ES2026, Node.js 24) eliminates the dependency |
+| `string-width` | `=4.2.2` | `^7.0.0` | Upgraded | Used only in @growi/editor (ESM context, Vite-bundled) |
+| `next-themes` | `^0.2.1` | `^0.4.6` | Upgraded | Original issue #122 was misattributed; only change needed: type import path |
+| `@keycloak/keycloak-admin-client` | `^18.0.0` | Unchanged | Deferred | API breaking changes (v18→v26) require separate migration effort |
+| `handsontable` | `=6.2.2` | Unchanged | Kept | v7.0.0+ is proprietary (non-MIT license) |
+| `@handsontable/react` | `=2.1.0` | Unchanged | Kept | Requires handsontable >= 7.0.0 |
+
+### Additional Changes
+
+- Added `RegExp.escape()` TypeScript type declarations in `apps/app/src/@types/`, `packages/core/src/@types/`, and `packages/remark-lsx/src/@types/` (awaiting TypeScript built-in support)
+- Updated `tsconfig.build.client.json` to include `src/@types/**/*.d.ts` for Next.js build compatibility
+- Updated `generate-children-regexp.spec.ts` test expectations for `RegExp.escape()` output (escapes spaces as `\x20`)
+- Removed `escape-string-regexp` from `transpilePackages` in `next.config.ts`
+- Updated `bootstrap` version across 5 packages: apps/app, packages/editor, packages/core-styles, packages/preset-themes, apps/slackbot-proxy
+- Updated `// comments for dependencies` to retain only `@keycloak` entry with updated reason

+ 3 - 3
.kiro/specs/upgrade-fixed-packages/spec.json

@@ -3,7 +3,7 @@
   "created_at": "2026-03-23T00:00:00Z",
   "created_at": "2026-03-23T00:00:00Z",
   "updated_at": "2026-03-23T00:00:00Z",
   "updated_at": "2026-03-23T00:00:00Z",
   "language": "en",
   "language": "en",
-  "phase": "tasks-generated",
+  "phase": "implementation-complete",
   "approvals": {
   "approvals": {
     "requirements": {
     "requirements": {
       "generated": true,
       "generated": true,
@@ -15,8 +15,8 @@
     },
     },
     "tasks": {
     "tasks": {
       "generated": true,
       "generated": true,
-      "approved": false
+      "approved": true
     }
     }
   },
   },
-  "ready_for_implementation": false
+  "ready_for_implementation": true
 }
 }

+ 18 - 18
.kiro/specs/upgrade-fixed-packages/tasks.md

@@ -1,29 +1,29 @@
 # Implementation Plan
 # Implementation Plan
 
 
-- [ ] 1. Pre-implementation verification
-- [ ] 1.1 Verify RegExp.escape() availability and TypeScript support
+- [x] 1. Pre-implementation verification
+- [x] 1.1 Verify RegExp.escape() availability and TypeScript support
   - Confirm `RegExp.escape()` is available at runtime in the project's Node.js 24 target
   - Confirm `RegExp.escape()` is available at runtime in the project's Node.js 24 target
   - Check whether TypeScript recognizes `RegExp.escape()` — may need `lib` config update or `@types/node` update
   - Check whether TypeScript recognizes `RegExp.escape()` — may need `lib` config update or `@types/node` update
   - If unavailable, fall back to upgrading `escape-string-regexp` to v5 with `require(esm)` instead
   - If unavailable, fall back to upgrading `escape-string-regexp` to v5 with `require(esm)` instead
   - _Requirements: 2.2_
   - _Requirements: 2.2_
 
 
-- [ ] 1.2 Review next-themes v0.3.0 and v0.4.0 breaking API changes
+- [x] 1.2 Review next-themes v0.3.0 and v0.4.0 breaking API changes
   - Read changelogs for v0.3.0 and v0.4.0 releases to identify breaking changes
   - Read changelogs for v0.3.0 and v0.4.0 releases to identify breaking changes
   - Map breaking changes to the 12 consuming files in apps/app
   - Map breaking changes to the 12 consuming files in apps/app
   - Determine migration effort and document required code changes
   - Determine migration effort and document required code changes
   - Confirm GROWI's Pages Router usage is unaffected by the cacheComponents bug (issue #375)
   - Confirm GROWI's Pages Router usage is unaffected by the cacheComponents bug (issue #375)
   - _Requirements: 1.2_
   - _Requirements: 1.2_
 
 
-- [ ] 2. Low-risk package upgrades
-- [ ] 2.1 (P) Relax @aws-sdk version range
-  - Change `@aws-sdk/client-s3` from `3.454.0` to `^3.454.0` in apps/app/package.json
-  - Change `@aws-sdk/s3-request-presigner` from `3.454.0` to `^3.454.0`
+- [x] 2. Low-risk package upgrades
+- [x] 2.1 (P) Relax @aws-sdk version range
+  - Change `@aws-sdk/client-s3` from `3.454.0` to `^3.1014.0` in apps/app/package.json
+  - Change `@aws-sdk/s3-request-presigner` from `3.454.0` to `^3.1014.0`
   - Update the misleading `"@aws-skd/*"` comment to reflect the actual reason or remove it
   - Update the misleading `"@aws-skd/*"` comment to reflect the actual reason or remove it
   - Run `pnpm install` and verify build with `turbo run build --filter @growi/app`
   - Run `pnpm install` and verify build with `turbo run build --filter @growi/app`
   - Run `turbo run test --filter @growi/app` to confirm no regressions
   - Run `turbo run test --filter @growi/app` to confirm no regressions
   - _Requirements: 1.3, 4.1, 4.2, 4.4_
   - _Requirements: 1.3, 4.1, 4.2, 4.4_
 
 
-- [ ] 2.2 (P) Upgrade string-width in @growi/editor
+- [x] 2.2 (P) Upgrade string-width in @growi/editor
   - Update `string-width` from `=4.2.2` to `^7.0.0` in packages/editor/package.json
   - Update `string-width` from `=4.2.2` to `^7.0.0` in packages/editor/package.json
   - Verify @growi/editor builds successfully (Vite, ESM context)
   - Verify @growi/editor builds successfully (Vite, ESM context)
   - Run `turbo run build --filter @growi/app` to confirm downstream build passes
   - Run `turbo run build --filter @growi/app` to confirm downstream build passes
@@ -31,8 +31,8 @@
   - Remove the `string-width` comment from apps/app/package.json `// comments for dependencies`
   - Remove the `string-width` comment from apps/app/package.json `// comments for dependencies`
   - _Requirements: 2.1, 2.3, 4.1, 4.2, 4.4_
   - _Requirements: 2.1, 2.3, 4.1, 4.2, 4.4_
 
 
-- [ ] 3. Upgrade bootstrap to ^5.3.4
-  - Change `bootstrap` from `=5.3.2` to `^5.3.4` in apps/app/package.json
+- [x] 3. Upgrade bootstrap to ^5.3.8
+  - Change `bootstrap` from `=5.3.2` to `^5.3.8` in apps/app/package.json and all other packages
   - Run `pnpm install` to resolve the new version
   - Run `pnpm install` to resolve the new version
   - Run `pnpm run pre:styles-commons` and `pnpm run pre:styles-components` to verify SCSS compilation
   - Run `pnpm run pre:styles-commons` and `pnpm run pre:styles-components` to verify SCSS compilation
   - Run `turbo run build --filter @growi/app` to confirm Turbopack build passes
   - Run `turbo run build --filter @growi/app` to confirm Turbopack build passes
@@ -43,15 +43,15 @@
   - If build or SCSS fails, revert and document the failure reason
   - If build or SCSS fails, revert and document the failure reason
   - _Requirements: 1.1, 4.1, 4.2, 4.3, 4.4, 4.5_
   - _Requirements: 1.1, 4.1, 4.2, 4.3, 4.4, 4.5_
 
 
-- [ ] 4. Replace escape-string-regexp with native RegExp.escape()
-- [ ] 4.1 Migrate all source files from escape-string-regexp to RegExp.escape()
+- [x] 4. Replace escape-string-regexp with native RegExp.escape()
+- [x] 4.1 Migrate all source files from escape-string-regexp to RegExp.escape()
   - Replace `import escapeStringRegexp from 'escape-string-regexp'` and corresponding calls with `RegExp.escape()` in each file
   - Replace `import escapeStringRegexp from 'escape-string-regexp'` and corresponding calls with `RegExp.escape()` in each file
   - Files in apps/app/src: page.ts, page/index.ts, page-grant.ts, users.js, obsolete-page.js, openai.ts (6 files)
   - Files in apps/app/src: page.ts, page/index.ts, page-grant.ts, users.js, obsolete-page.js, openai.ts (6 files)
   - Files in packages: core/src/utils/page-path-utils (2 files), remark-lsx/src/server/routes/list-pages/index.ts (1 file)
   - Files in packages: core/src/utils/page-path-utils (2 files), remark-lsx/src/server/routes/list-pages/index.ts (1 file)
   - Ensure each replacement preserves the exact same escaping behavior
   - Ensure each replacement preserves the exact same escaping behavior
   - _Requirements: 4.1_
   - _Requirements: 4.1_
 
 
-- [ ] 4.2 Remove escape-string-regexp dependency and verify
+- [x] 4.2 Remove escape-string-regexp dependency and verify
   - Remove `escape-string-regexp` from apps/app/package.json dependencies
   - Remove `escape-string-regexp` from apps/app/package.json dependencies
   - Remove from packages/core and packages/remark-lsx package.json if listed
   - Remove from packages/core and packages/remark-lsx package.json if listed
   - Remove the `escape-string-regexp` comment from `// comments for dependencies`
   - Remove the `escape-string-regexp` comment from `// comments for dependencies`
@@ -63,15 +63,15 @@
   - If RegExp.escape() has TypeScript issues, add type declaration or adjust lib config
   - If RegExp.escape() has TypeScript issues, add type declaration or adjust lib config
   - _Requirements: 2.1, 2.2, 4.1, 4.2, 4.3, 4.4, 4.5_
   - _Requirements: 2.1, 2.2, 4.1, 4.2, 4.3, 4.4, 4.5_
 
 
-- [ ] 5. Upgrade next-themes to ^0.4.x
-- [ ] 5.1 Update next-themes and adapt consuming code
-  - Change `next-themes` from `^0.2.1` to `^0.4.4` in apps/app/package.json
+- [x] 5. Upgrade next-themes to ^0.4.x
+- [x] 5.1 Update next-themes and adapt consuming code
+  - Change `next-themes` from `^0.2.1` to `^0.4.6` in apps/app/package.json
   - Apply required API migration changes across the 12 consuming files identified in design
   - Apply required API migration changes across the 12 consuming files identified in design
   - Pay attention to any renamed exports, changed hook signatures, or provider prop changes
   - Pay attention to any renamed exports, changed hook signatures, or provider prop changes
   - Ensure `useTheme()` and `ThemeProvider` usage is compatible with v0.4.x API
   - Ensure `useTheme()` and `ThemeProvider` usage is compatible with v0.4.x API
   - _Requirements: 1.2, 4.1_
   - _Requirements: 1.2, 4.1_
 
 
-- [ ] 5.2 Verify next-themes upgrade
+- [x] 5.2 Verify next-themes upgrade
   - Run `turbo run build --filter @growi/app` to confirm build passes
   - Run `turbo run build --filter @growi/app` to confirm build passes
   - Run `turbo run lint --filter @growi/app` to check for type errors (original pinning was about types)
   - Run `turbo run lint --filter @growi/app` to check for type errors (original pinning was about types)
   - Run `turbo run test --filter @growi/app` to confirm no regressions
   - Run `turbo run test --filter @growi/app` to confirm no regressions
@@ -80,7 +80,7 @@
   - If upgrade fails, revert and document the reason; keep the pin with an updated comment
   - If upgrade fails, revert and document the reason; keep the pin with an updated comment
   - _Requirements: 4.2, 4.3, 4.4, 4.5, 4.6_
   - _Requirements: 4.2, 4.3, 4.4, 4.5, 4.6_
 
 
-- [ ] 6. Finalize audit documentation and comment blocks
+- [x] 6. Finalize audit documentation and comment blocks
   - Verify `// comments for dependencies` block contains only packages that remain pinned (@keycloak if unchanged)
   - Verify `// comments for dependencies` block contains only packages that remain pinned (@keycloak if unchanged)
   - Verify `// comments for defDependencies` block is accurate (handsontable entries unchanged)
   - Verify `// comments for defDependencies` block is accurate (handsontable entries unchanged)
   - Update comment text to reflect current reasons where applicable
   - Update comment text to reflect current reasons where applicable

+ 0 - 1
apps/app/next.config.ts

@@ -28,7 +28,6 @@ const getTranspilePackages = (): string[] => {
     'decode-named-character-reference',
     'decode-named-character-reference',
     'devlop',
     'devlop',
     'fault',
     'fault',
-    'escape-string-regexp',
     'hastscript',
     'hastscript',
     'html-void-elements',
     'html-void-elements',
     'is-absolute-url',
     'is-absolute-url',

+ 5 - 11
apps/app/package.json

@@ -56,17 +56,12 @@
     "version:premajor": "pnpm version premajor --preid=RC"
     "version:premajor": "pnpm version premajor --preid=RC"
   },
   },
   "// comments for dependencies": {
   "// comments for dependencies": {
-    "@aws-skd/*": "fix version above 3.186.0 that is required by mongodb@4.16.0",
-    "@keycloak/keycloak-admin-client": "19.0.0 or above exports only ESM.",
-    "bootstrap": "v5.3.3 has a bug. refs: https://github.com/twbs/bootstrap/issues/39798",
-    "escape-string-regexp": "5.0.0 or above exports only ESM",
-    "next-themes": "0.3.0 causes a type error: https://github.com/pacocoursey/next-themes/issues/122",
-    "string-width": "5.0.0 or above exports only ESM."
+    "@keycloak/keycloak-admin-client": "19.0.0 or above exports only ESM. API breaking changes require separate migration effort."
   },
   },
   "dependencies": {
   "dependencies": {
     "@akebifiky/remark-simple-plantuml": "^1.0.2",
     "@akebifiky/remark-simple-plantuml": "^1.0.2",
-    "@aws-sdk/client-s3": "3.454.0",
-    "@aws-sdk/s3-request-presigner": "3.454.0",
+    "@aws-sdk/client-s3": "^3.1014.0",
+    "@aws-sdk/s3-request-presigner": "^3.1014.0",
     "@azure/identity": "^4.4.1",
     "@azure/identity": "^4.4.1",
     "@azure/openai": "^2.0.0",
     "@azure/openai": "^2.0.0",
     "@azure/storage-blob": "^12.16.0",
     "@azure/storage-blob": "^12.16.0",
@@ -129,7 +124,7 @@
     "axios-retry": "^3.2.4",
     "axios-retry": "^3.2.4",
     "babel-plugin-superjson-next": "^0.4.2",
     "babel-plugin-superjson-next": "^0.4.2",
     "body-parser": "^1.20.3",
     "body-parser": "^1.20.3",
-    "bootstrap": "=5.3.2",
+    "bootstrap": "^5.3.8",
     "browser-bunyan": "^1.8.0",
     "browser-bunyan": "^1.8.0",
     "bson-objectid": "^2.0.4",
     "bson-objectid": "^2.0.4",
     "bunyan": "^1.8.15",
     "bunyan": "^1.8.15",
@@ -152,7 +147,6 @@
     "dotenv-flow": "^3.2.0",
     "dotenv-flow": "^3.2.0",
     "downshift": "^8.2.3",
     "downshift": "^8.2.3",
     "ejs": "^3.1.10",
     "ejs": "^3.1.10",
-    "escape-string-regexp": "^4.0.0",
     "expose-gc": "^1.0.0",
     "expose-gc": "^1.0.0",
     "express": "^4.20.0",
     "express": "^4.20.0",
     "express-bunyan-logger": "^1.3.3",
     "express-bunyan-logger": "^1.3.3",
@@ -205,7 +199,7 @@
     "next": "^16.2.1",
     "next": "^16.2.1",
     "next-dynamic-loading-props": "^0.1.1",
     "next-dynamic-loading-props": "^0.1.1",
     "next-i18next": "^15.3.1",
     "next-i18next": "^15.3.1",
-    "next-themes": "^0.2.1",
+    "next-themes": "^0.4.6",
     "nocache": "^4.0.0",
     "nocache": "^4.0.0",
     "node-cron": "^3.0.2",
     "node-cron": "^3.0.2",
     "nodemailer": "^6.9.15",
     "nodemailer": "^6.9.15",

+ 1 - 2
apps/app/src/features/openai/server/services/openai.ts

@@ -13,7 +13,6 @@ import {
 } from '@growi/core';
 } from '@growi/core';
 import { deepEquals } from '@growi/core/dist/utils';
 import { deepEquals } from '@growi/core/dist/utils';
 import { isGlobPatternPath } from '@growi/core/dist/utils/page-path-utils';
 import { isGlobPatternPath } from '@growi/core/dist/utils/page-path-utils';
-import escapeStringRegexp from 'escape-string-regexp';
 import createError from 'http-errors';
 import createError from 'http-errors';
 import mongoose, { type HydratedDocument, type Types } from 'mongoose';
 import mongoose, { type HydratedDocument, type Types } from 'mongoose';
 import type { OpenAI } from 'openai';
 import type { OpenAI } from 'openai';
@@ -78,7 +77,7 @@ const convertPathPatternsToRegExp = (
   return pagePathPatterns.map((pagePathPattern) => {
   return pagePathPatterns.map((pagePathPattern) => {
     if (isGlobPatternPath(pagePathPattern)) {
     if (isGlobPatternPath(pagePathPattern)) {
       const trimedPagePathPattern = pagePathPattern.replace('/*', '');
       const trimedPagePathPattern = pagePathPattern.replace('/*', '');
-      const escapedPagePathPattern = escapeStringRegexp(trimedPagePathPattern);
+      const escapedPagePathPattern = RegExp.escape(trimedPagePathPattern);
       // https://regex101.com/r/x5KIZL/1
       // https://regex101.com/r/x5KIZL/1
       return new RegExp(`^${escapedPagePathPattern}($|/)`);
       return new RegExp(`^${escapedPagePathPattern}($|/)`);
     }
     }

+ 1 - 2
apps/app/src/server/models/obsolete-page.js

@@ -7,7 +7,6 @@ import {
 import { isUserPage } from '@growi/core/dist/utils/page-path-utils';
 import { isUserPage } from '@growi/core/dist/utils/page-path-utils';
 import { removeHeadingSlash } from '@growi/core/dist/utils/path-utils';
 import { removeHeadingSlash } from '@growi/core/dist/utils/path-utils';
 import { differenceInYears } from 'date-fns/differenceInYears';
 import { differenceInYears } from 'date-fns/differenceInYears';
-import escapeStringRegexp from 'escape-string-regexp';
 
 
 import { Comment } from '~/features/comment/server/models/comment';
 import { Comment } from '~/features/comment/server/models/comment';
 import ExternalUserGroup from '~/features/external-user-group/server/models/external-user-group';
 import ExternalUserGroup from '~/features/external-user-group/server/models/external-user-group';
@@ -688,7 +687,7 @@ export const getPageSchema = (crowi) => {
     const regexpList = pathList.map((path) => {
     const regexpList = pathList.map((path) => {
       const pathWithTrailingSlash = pathUtils.addTrailingSlash(path);
       const pathWithTrailingSlash = pathUtils.addTrailingSlash(path);
       return new RegExp(
       return new RegExp(
-        `^${escapeStringRegexp(pathWithTrailingSlash)}_{1,2}template$`,
+        `^${RegExp.escape(pathWithTrailingSlash)}_{1,2}template$`,
       );
       );
     });
     });
 
 

+ 7 - 8
apps/app/src/server/models/page.ts

@@ -10,7 +10,6 @@ import {
   normalizePath,
   normalizePath,
 } from '@growi/core/dist/utils/path-utils';
 } from '@growi/core/dist/utils/path-utils';
 import assert from 'assert';
 import assert from 'assert';
-import escapeStringRegexp from 'escape-string-regexp';
 import type mongoose from 'mongoose';
 import type mongoose from 'mongoose';
 import type {
 import type {
   AnyObject,
   AnyObject,
@@ -348,7 +347,7 @@ export class PageQueryBuilder {
     const pathNormalized = normalizePath(path);
     const pathNormalized = normalizePath(path);
     const pathWithTrailingSlash = addTrailingSlash(path);
     const pathWithTrailingSlash = addTrailingSlash(path);
 
 
-    const startsPattern = escapeStringRegexp(pathWithTrailingSlash);
+    const startsPattern = RegExp.escape(pathWithTrailingSlash);
 
 
     this.query = this.query.and({
     this.query = this.query.and({
       $or: [
       $or: [
@@ -373,7 +372,7 @@ export class PageQueryBuilder {
 
 
     const pathWithTrailingSlash = addTrailingSlash(path);
     const pathWithTrailingSlash = addTrailingSlash(path);
 
 
-    const startsPattern = escapeStringRegexp(pathWithTrailingSlash);
+    const startsPattern = RegExp.escape(pathWithTrailingSlash);
 
 
     this.query = this.query.and({ path: new RegExp(`^${startsPattern}`) });
     this.query = this.query.and({ path: new RegExp(`^${startsPattern}`) });
 
 
@@ -409,7 +408,7 @@ export class PageQueryBuilder {
       return this;
       return this;
     }
     }
 
 
-    const startsPattern = escapeStringRegexp(path);
+    const startsPattern = RegExp.escape(path);
 
 
     this.query = this.query.and({ path: new RegExp(`^${startsPattern}`) });
     this.query = this.query.and({ path: new RegExp(`^${startsPattern}`) });
 
 
@@ -424,7 +423,7 @@ export class PageQueryBuilder {
       return this;
       return this;
     }
     }
 
 
-    const startsPattern = escapeStringRegexp(str);
+    const startsPattern = RegExp.escape(str);
 
 
     this.query = this.query.and({
     this.query = this.query.and({
       path: new RegExp(`^(?!${startsPattern}).*$`),
       path: new RegExp(`^(?!${startsPattern}).*$`),
@@ -440,7 +439,7 @@ export class PageQueryBuilder {
       return this;
       return this;
     }
     }
 
 
-    const startsPattern = escapeStringRegexp(path);
+    const startsPattern = RegExp.escape(path);
 
 
     this.query = this.query.and({
     this.query = this.query.and({
       path: { $not: new RegExp(`^${startsPattern}(/|$)`) },
       path: { $not: new RegExp(`^${startsPattern}(/|$)`) },
@@ -455,7 +454,7 @@ export class PageQueryBuilder {
       return this;
       return this;
     }
     }
 
 
-    const match = escapeStringRegexp(str);
+    const match = RegExp.escape(str);
 
 
     this.query = this.query.and({ path: new RegExp(`^(?=.*${match}).*$`) });
     this.query = this.query.and({ path: new RegExp(`^(?=.*${match}).*$`) });
 
 
@@ -468,7 +467,7 @@ export class PageQueryBuilder {
       return this;
       return this;
     }
     }
 
 
-    const match = escapeStringRegexp(str);
+    const match = RegExp.escape(str);
 
 
     this.query = this.query.and({ path: new RegExp(`^(?!.*${match}).*$`) });
     this.query = this.query.and({ path: new RegExp(`^(?!.*${match}).*$`) });
 
 

+ 1 - 2
apps/app/src/server/routes/apiv3/users.js

@@ -2,7 +2,6 @@ import { SCOPE } from '@growi/core/dist/interfaces';
 import { ErrorV3 } from '@growi/core/dist/models';
 import { ErrorV3 } from '@growi/core/dist/models';
 import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
 import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
 import { userHomepagePath } from '@growi/core/dist/utils/page-path-utils';
 import { userHomepagePath } from '@growi/core/dist/utils/page-path-utils';
-import escapeStringRegexp from 'escape-string-regexp';
 import express from 'express';
 import express from 'express';
 import { body, query } from 'express-validator';
 import { body, query } from 'express-validator';
 import path from 'pathe';
 import path from 'pathe';
@@ -336,7 +335,7 @@ module.exports = (crowi) => {
 
 
       // Search from input
       // Search from input
       const searchText = req.query.searchText || '';
       const searchText = req.query.searchText || '';
-      const searchWord = new RegExp(escapeStringRegexp(searchText));
+      const searchWord = new RegExp(RegExp.escape(searchText));
       // Sort
       // Sort
       const { sort, sortOrder } = req.query;
       const { sort, sortOrder } = req.query;
       const sortOutput = {
       const sortOutput = {

+ 1 - 5
apps/app/src/server/service/page-grant.ts

@@ -6,7 +6,6 @@ import {
   PageGrant,
   PageGrant,
 } from '@growi/core';
 } from '@growi/core';
 import { pagePathUtils, pageUtils, pathUtils } from '@growi/core/dist/utils';
 import { pagePathUtils, pageUtils, pathUtils } from '@growi/core/dist/utils';
-import escapeStringRegexp from 'escape-string-regexp';
 import mongoose, { type HydratedDocument } from 'mongoose';
 import mongoose, { type HydratedDocument } from 'mongoose';
 
 
 import type { ExternalGroupProviderType } from '~/features/external-user-group/interfaces/external-user-group';
 import type { ExternalGroupProviderType } from '~/features/external-user-group/interfaces/external-user-group';
@@ -590,10 +589,7 @@ class PageGrantService implements IPageGrantService {
     };
     };
 
 
     const commonCondition = {
     const commonCondition = {
-      path: new RegExp(
-        `^${escapeStringRegexp(addTrailingSlash(targetPath))}`,
-        'i',
-      ),
+      path: new RegExp(`^${RegExp.escape(addTrailingSlash(targetPath))}`, 'i'),
       isEmpty: false,
       isEmpty: false,
     };
     };
 
 

+ 9 - 18
apps/app/src/server/service/page/index.ts

@@ -18,7 +18,6 @@ import type {
 } from '@growi/core/dist/interfaces';
 } from '@growi/core/dist/interfaces';
 import { PageGrant } from '@growi/core/dist/interfaces';
 import { PageGrant } from '@growi/core/dist/interfaces';
 import { pagePathUtils, pathUtils } from '@growi/core/dist/utils';
 import { pagePathUtils, pathUtils } from '@growi/core/dist/utils';
-import escapeStringRegexp from 'escape-string-regexp';
 import type EventEmitter from 'events';
 import type EventEmitter from 'events';
 import type { Cursor, HydratedDocument } from 'mongoose';
 import type { Cursor, HydratedDocument } from 'mongoose';
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
@@ -941,7 +940,7 @@ class PageService implements IPageService {
   }
   }
 
 
   private isRenamingToUnderTarget(fromPath: string, toPath: string): boolean {
   private isRenamingToUnderTarget(fromPath: string, toPath: string): boolean {
-    const pathToTest = escapeStringRegexp(addTrailingSlash(fromPath));
+    const pathToTest = RegExp.escape(addTrailingSlash(fromPath));
     const pathToBeTested = toPath;
     const pathToBeTested = toPath;
 
 
     return new RegExp(`^${pathToTest}`, 'i').test(pathToBeTested);
     return new RegExp(`^${pathToTest}`, 'i').test(pathToBeTested);
@@ -1245,10 +1244,7 @@ class PageService implements IPageService {
     const batchStream = createBatchStream(BULK_REINDEX_SIZE);
     const batchStream = createBatchStream(BULK_REINDEX_SIZE);
 
 
     const newPagePathPrefix = newPagePathSanitized;
     const newPagePathPrefix = newPagePathSanitized;
-    const pathRegExp = new RegExp(
-      `^${escapeStringRegexp(targetPage.path)}`,
-      'i',
-    );
+    const pathRegExp = new RegExp(`^${RegExp.escape(targetPage.path)}`, 'i');
 
 
     const renameDescendants = this.renameDescendants.bind(this);
     const renameDescendants = this.renameDescendants.bind(this);
     const pageEvent = this.pageEvent;
     const pageEvent = this.pageEvent;
@@ -1304,10 +1300,7 @@ class PageService implements IPageService {
     const batchStream = createBatchStream(BULK_REINDEX_SIZE);
     const batchStream = createBatchStream(BULK_REINDEX_SIZE);
 
 
     const newPagePathPrefix = newPagePathSanitized;
     const newPagePathPrefix = newPagePathSanitized;
-    const pathRegExp = new RegExp(
-      `^${escapeStringRegexp(targetPage.path)}`,
-      'i',
-    );
+    const pathRegExp = new RegExp(`^${RegExp.escape(targetPage.path)}`, 'i');
 
 
     const renameDescendants = this.renameDescendants.bind(this);
     const renameDescendants = this.renameDescendants.bind(this);
     const pageEvent = this.pageEvent;
     const pageEvent = this.pageEvent;
@@ -1892,7 +1885,7 @@ class PageService implements IPageService {
     const batchStream = createBatchStream(BULK_REINDEX_SIZE);
     const batchStream = createBatchStream(BULK_REINDEX_SIZE);
 
 
     const newPagePathPrefix = newPagePathSanitized;
     const newPagePathPrefix = newPagePathSanitized;
-    const pathRegExp = new RegExp(`^${escapeStringRegexp(page.path)}`, 'i');
+    const pathRegExp = new RegExp(`^${RegExp.escape(page.path)}`, 'i');
 
 
     const duplicateDescendants = this.duplicateDescendants.bind(this);
     const duplicateDescendants = this.duplicateDescendants.bind(this);
     const pageEvent = this.pageEvent;
     const pageEvent = this.pageEvent;
@@ -1948,7 +1941,7 @@ class PageService implements IPageService {
     const batchStream = createBatchStream(BULK_REINDEX_SIZE);
     const batchStream = createBatchStream(BULK_REINDEX_SIZE);
 
 
     const newPagePathPrefix = newPagePathSanitized;
     const newPagePathPrefix = newPagePathSanitized;
-    const pathRegExp = new RegExp(`^${escapeStringRegexp(page.path)}`, 'i');
+    const pathRegExp = new RegExp(`^${RegExp.escape(page.path)}`, 'i');
 
 
     const duplicateDescendants = this.duplicateDescendants.bind(this);
     const duplicateDescendants = this.duplicateDescendants.bind(this);
     const pageEvent = this.pageEvent;
     const pageEvent = this.pageEvent;
@@ -3968,7 +3961,7 @@ class PageService implements IPageService {
     const ancestorPaths = paths.flatMap((p) => collectAncestorPaths(p, []));
     const ancestorPaths = paths.flatMap((p) => collectAncestorPaths(p, []));
     // targets' descendants
     // targets' descendants
     const pathAndRegExpsToNormalize: (RegExp | string)[] = paths.map(
     const pathAndRegExpsToNormalize: (RegExp | string)[] = paths.map(
-      (p) => new RegExp(`^${escapeStringRegexp(addTrailingSlash(p))}`, 'i'),
+      (p) => new RegExp(`^${RegExp.escape(addTrailingSlash(p))}`, 'i'),
     );
     );
     // include targets' path
     // include targets' path
     pathAndRegExpsToNormalize.push(...paths);
     pathAndRegExpsToNormalize.push(...paths);
@@ -4179,7 +4172,7 @@ class PageService implements IPageService {
           const parentId = parent._id;
           const parentId = parent._id;
 
 
           // Build filter
           // Build filter
-          const parentPathEscaped = escapeStringRegexp(
+          const parentPathEscaped = RegExp.escape(
             parent.path === '/' ? '' : parent.path,
             parent.path === '/' ? '' : parent.path,
           ); // adjust the path for RegExp
           ); // adjust the path for RegExp
           const filter: any = {
           const filter: any = {
@@ -5148,9 +5141,7 @@ class PageService implements IPageService {
     const wasOnTree = exPage.parent != null || isTopPage(exPage.path);
     const wasOnTree = exPage.parent != null || isTopPage(exPage.path);
     const shouldBeOnTree = currentPage.grant !== PageGrant.GRANT_RESTRICTED;
     const shouldBeOnTree = currentPage.grant !== PageGrant.GRANT_RESTRICTED;
     const isChildrenExist = await Page.count({
     const isChildrenExist = await Page.count({
-      path: new RegExp(
-        `^${escapeStringRegexp(addTrailingSlash(currentPage.path))}`,
-      ),
+      path: new RegExp(`^${RegExp.escape(addTrailingSlash(currentPage.path))}`),
       parent: { $ne: null },
       parent: { $ne: null },
     });
     });
 
 
@@ -5282,7 +5273,7 @@ class PageService implements IPageService {
     const shouldBeOnTree = grant !== PageGrant.GRANT_RESTRICTED;
     const shouldBeOnTree = grant !== PageGrant.GRANT_RESTRICTED;
     const isChildrenExist = await Page.count({
     const isChildrenExist = await Page.count({
       path: new RegExp(
       path: new RegExp(
-        `^${escapeStringRegexp(addTrailingSlash(clonedPageData.path))}`,
+        `^${RegExp.escape(addTrailingSlash(clonedPageData.path))}`,
       ),
       ),
       parent: { $ne: null },
       parent: { $ne: null },
     });
     });

+ 1 - 1
apps/app/src/stores-universal/use-next-themes.tsx

@@ -1,7 +1,7 @@
 import { ColorScheme } from '@growi/core';
 import { ColorScheme } from '@growi/core';
 import { isClient } from '@growi/core/dist/utils';
 import { isClient } from '@growi/core/dist/utils';
+import type { ThemeProviderProps, UseThemeProps } from 'next-themes';
 import { ThemeProvider, useTheme } from 'next-themes';
 import { ThemeProvider, useTheme } from 'next-themes';
-import type { ThemeProviderProps, UseThemeProps } from 'next-themes/dist/types';
 
 
 import { useForcedColorScheme } from '~/states/global';
 import { useForcedColorScheme } from '~/states/global';
 
 

+ 1 - 1
apps/app/tsconfig.build.client.json

@@ -1,7 +1,7 @@
 {
 {
   "$schema": "http://json.schemastore.org/tsconfig",
   "$schema": "http://json.schemastore.org/tsconfig",
   "extends": "./tsconfig.json",
   "extends": "./tsconfig.json",
-  "include": [".next/types/**/*.ts"],
+  "include": [".next/types/**/*.ts", "src/@types/**/*.d.ts"],
   "compilerOptions": {
   "compilerOptions": {
     "strict": false,
     "strict": false,
     "strictNullChecks": true,
     "strictNullChecks": true,

+ 2 - 3
apps/slackbot-proxy/package.json

@@ -68,8 +68,7 @@
   },
   },
   "// comments for devDependencies": {
   "// comments for devDependencies": {
     "@tsed/*": "v6.133.1 causes 'TypeError: Cannot read properties of undefined (reading 'prototype')' with `@Middleware()`",
     "@tsed/*": "v6.133.1 causes 'TypeError: Cannot read properties of undefined (reading 'prototype')' with `@Middleware()`",
-    "@tsed/core,exceptions": "force package to local node_modules in tsconfig.json since pnpm reads wrong hoisted tsed version (https://github.com/pnpm/pnpm/issues/7158)",
-    "bootstrap": "v5.3.3 has a bug. refs: https://github.com/twbs/bootstrap/issues/39798"
+    "@tsed/core,exceptions": "force package to local node_modules in tsconfig.json since pnpm reads wrong hoisted tsed version (https://github.com/pnpm/pnpm/issues/7158)"
   },
   },
   "devDependencies": {
   "devDependencies": {
     "@popperjs/core": "^2.11.8",
     "@popperjs/core": "^2.11.8",
@@ -77,7 +76,7 @@
     "@tsed/exceptions": "=6.43.0",
     "@tsed/exceptions": "=6.43.0",
     "@tsed/json-mapper": "=6.43.0",
     "@tsed/json-mapper": "=6.43.0",
     "@types/bunyan": "^1.8.11",
     "@types/bunyan": "^1.8.11",
-    "bootstrap": "=5.3.2",
+    "bootstrap": "^5.3.8",
     "browser-bunyan": "^1.6.3",
     "browser-bunyan": "^1.6.3",
     "morgan": "^1.10.0"
     "morgan": "^1.10.0"
   }
   }

+ 2 - 4
packages/core-styles/package.json

@@ -17,11 +17,9 @@
     "lint": "npm-run-all -p lint:*"
     "lint": "npm-run-all -p lint:*"
   },
   },
   "dependencies": {},
   "dependencies": {},
-  "// comments for defDependencies": {
-    "bootstrap": "v5.3.3 has a bug. refs: https://github.com/twbs/bootstrap/issues/39798"
-  },
+  "// comments for defDependencies": {},
   "devDependencies": {
   "devDependencies": {
-    "bootstrap": "=5.3.2"
+    "bootstrap": "^5.3.8"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@popperjs/core": "^2.11.8"
     "@popperjs/core": "^2.11.8"

+ 2 - 5
packages/core/package.json

@@ -69,12 +69,9 @@
     "lint": "npm-run-all -p lint:*",
     "lint": "npm-run-all -p lint:*",
     "test": "vitest run --coverage"
     "test": "vitest run --coverage"
   },
   },
-  "// comments for dependencies": {
-    "escape-string-regexp": "5.0.0 or above exports only ESM"
-  },
+  "// comments for dependencies": {},
   "dependencies": {
   "dependencies": {
-    "bson-objectid": "^2.0.4",
-    "escape-string-regexp": "^4.0.0"
+    "bson-objectid": "^2.0.4"
   },
   },
   "devDependencies": {
   "devDependencies": {
     "@types/express": "^4",
     "@types/express": "^4",

+ 9 - 0
packages/core/src/index.ts

@@ -1,2 +1,11 @@
 export * from './consts';
 export * from './consts';
 export * from './interfaces';
 export * from './interfaces';
+
+// Type declaration for RegExp.escape() (ES2026, Stage 4)
+// Available natively in Node.js 24+ (V8 13.x+)
+// Can be removed once TypeScript adds built-in support
+declare global {
+  interface RegExpConstructor {
+    escape(str: string): string;
+  }
+}

+ 2 - 2
packages/core/src/utils/page-path-utils/generate-children-regexp.spec.ts

@@ -18,7 +18,7 @@ describe('generateChildrenRegExp', () => {
     },
     },
     {
     {
       path: '/parent (with brackets)',
       path: '/parent (with brackets)',
-      expected: '^\\/parent \\(with brackets\\)(\\/[^/]+)\\/?$',
+      expected: '^\\/parent\\x20\\(with\\x20brackets\\)(\\/[^/]+)\\/?$',
       validPaths: [
       validPaths: [
         '/parent (with brackets)/child',
         '/parent (with brackets)/child',
         '/parent (with brackets)/test',
         '/parent (with brackets)/test',
@@ -30,7 +30,7 @@ describe('generateChildrenRegExp', () => {
     },
     },
     {
     {
       path: '/parent[with square]',
       path: '/parent[with square]',
-      expected: '^\\/parent\\[with square\\](\\/[^/]+)\\/?$',
+      expected: '^\\/parent\\[with\\x20square\\](\\/[^/]+)\\/?$',
       validPaths: ['/parent[with square]/child', '/parent[with square]/test'],
       validPaths: ['/parent[with square]/child', '/parent[with square]/test'],
       invalidPaths: [
       invalidPaths: [
         '/parent[with square]',
         '/parent[with square]',

+ 1 - 3
packages/core/src/utils/page-path-utils/generate-children-regexp.ts

@@ -1,5 +1,3 @@
-import escapeStringRegexp from 'escape-string-regexp';
-
 import { isTopPage } from './is-top-page';
 import { isTopPage } from './is-top-page';
 
 
 /**
 /**
@@ -12,5 +10,5 @@ export const generateChildrenRegExp = (path: string): RegExp => {
 
 
   // https://regex101.com/r/mrDJrx/1
   // https://regex101.com/r/mrDJrx/1
   // ex. /parent/any_child OR /any_level1
   // ex. /parent/any_child OR /any_level1
-  return new RegExp(`^${escapeStringRegexp(path)}(\\/[^/]+)\\/?$`);
+  return new RegExp(`^${RegExp.escape(path)}(\\/[^/]+)\\/?$`);
 };
 };

+ 4 - 9
packages/core/src/utils/page-path-utils/index.ts

@@ -1,5 +1,3 @@
-import escapeStringRegexp from 'escape-string-regexp';
-
 import { isValidObjectId } from '../objectid-utils';
 import { isValidObjectId } from '../objectid-utils';
 import { addTrailingSlash } from '../path-utils';
 import { addTrailingSlash } from '../path-utils';
 import { isTopPage as _isTopPage } from './is-top-page';
 import { isTopPage as _isTopPage } from './is-top-page';
@@ -149,7 +147,7 @@ export const convertToNewAffiliationPath = (
   if (newPath == null) {
   if (newPath == null) {
     throw new Error('Please input the new page path');
     throw new Error('Please input the new page path');
   }
   }
-  const pathRegExp = new RegExp(`^${escapeStringRegexp(oldPath)}`, 'i');
+  const pathRegExp = new RegExp(`^${RegExp.escape(oldPath)}`, 'i');
   return childPath.replace(pathRegExp, newPath);
   return childPath.replace(pathRegExp, newPath);
 };
 };
 
 
@@ -239,8 +237,8 @@ export const isEitherOfPathAreaOverlap = (
   const path1WithSlash = addTrailingSlash(path1);
   const path1WithSlash = addTrailingSlash(path1);
   const path2WithSlash = addTrailingSlash(path2);
   const path2WithSlash = addTrailingSlash(path2);
 
 
-  const path1Area = new RegExp(`^${escapeStringRegexp(path1WithSlash)}`, 'i');
-  const path2Area = new RegExp(`^${escapeStringRegexp(path2WithSlash)}`, 'i');
+  const path1Area = new RegExp(`^${RegExp.escape(path1WithSlash)}`, 'i');
+  const path2Area = new RegExp(`^${RegExp.escape(path2WithSlash)}`, 'i');
 
 
   if (path1Area.test(path2) || path2Area.test(path1)) {
   if (path1Area.test(path2) || path2Area.test(path1)) {
     return true;
     return true;
@@ -266,10 +264,7 @@ export const isPathAreaOverlap = (
 
 
   const pathWithSlash = addTrailingSlash(pathToTest);
   const pathWithSlash = addTrailingSlash(pathToTest);
 
 
-  const pathAreaToTest = new RegExp(
-    `^${escapeStringRegexp(pathWithSlash)}`,
-    'i',
-  );
+  const pathAreaToTest = new RegExp(`^${RegExp.escape(pathWithSlash)}`, 'i');
   if (pathAreaToTest.test(pathToBeTested)) {
   if (pathAreaToTest.test(pathToBeTested)) {
     return true;
     return true;
   }
   }

+ 3 - 5
packages/editor/package.json

@@ -24,9 +24,7 @@
     "react": "^18.2.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0"
     "react-dom": "^18.2.0"
   },
   },
-  "// comments for devDependencies": {
-    "string-width": "5.0.0 or above exports only ESM."
-  },
+  "// comments for devDependencies": {},
   "devDependencies": {
   "devDependencies": {
     "@codemirror/autocomplete": "^6.18.4",
     "@codemirror/autocomplete": "^6.18.4",
     "@codemirror/commands": "^6.8.0",
     "@codemirror/commands": "^6.8.0",
@@ -52,7 +50,7 @@
     "@uiw/codemirror-theme-kimbie": "^4.23.8",
     "@uiw/codemirror-theme-kimbie": "^4.23.8",
     "@uiw/codemirror-themes": "^4.23.8",
     "@uiw/codemirror-themes": "^4.23.8",
     "@uiw/react-codemirror": "^4.23.8",
     "@uiw/react-codemirror": "^4.23.8",
-    "bootstrap": "=5.3.2",
+    "bootstrap": "^5.3.8",
     "cm6-theme-basic-light": "^0.2.0",
     "cm6-theme-basic-light": "^0.2.0",
     "cm6-theme-material-dark": "^0.2.0",
     "cm6-theme-material-dark": "^0.2.0",
     "cm6-theme-nord": "^0.2.0",
     "cm6-theme-nord": "^0.2.0",
@@ -67,7 +65,7 @@
     "react-hook-form": "^7.45.4",
     "react-hook-form": "^7.45.4",
     "react-toastify": "^9.1.3",
     "react-toastify": "^9.1.3",
     "reactstrap": "^9.2.2",
     "reactstrap": "^9.2.2",
-    "string-width": "=4.2.2",
+    "string-width": "^7.0.0",
     "simplebar-react": "^2.3.6",
     "simplebar-react": "^2.3.6",
     "socket.io": "^4.7.5",
     "socket.io": "^4.7.5",
     "socket.io-client": "^4.7.5",
     "socket.io-client": "^4.7.5",

+ 1 - 1
packages/preset-themes/package.json

@@ -31,7 +31,7 @@
   "devDependencies": {
   "devDependencies": {
     "@growi/core": "workspace:^",
     "@growi/core": "workspace:^",
     "@growi/core-styles": "workspace:^",
     "@growi/core-styles": "workspace:^",
-    "bootstrap": "=5.3.2",
+    "bootstrap": "^5.3.8",
     "sass": "^1.55.0"
     "sass": "^1.55.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {

+ 1 - 4
packages/remark-lsx/package.json

@@ -29,14 +29,11 @@
     "lint": "run-p lint:*",
     "lint": "run-p lint:*",
     "test": "vitest run --coverage"
     "test": "vitest run --coverage"
   },
   },
-  "// comments for dependencies": {
-    "escape-string-regexp": "5.0.0 or above exports only ESM"
-  },
+  "// comments for dependencies": {},
   "dependencies": {
   "dependencies": {
     "@growi/core": "workspace:^",
     "@growi/core": "workspace:^",
     "@growi/remark-growi-directive": "workspace:^",
     "@growi/remark-growi-directive": "workspace:^",
     "@growi/ui": "workspace:^",
     "@growi/ui": "workspace:^",
-    "escape-string-regexp": "^4.0.0",
     "express": "^4.20.0",
     "express": "^4.20.0",
     "express-validator": "^6.14.0",
     "express-validator": "^6.14.0",
     "http-errors": "^2.0.0",
     "http-errors": "^2.0.0",

+ 4 - 5
packages/remark-lsx/src/server/routes/list-pages/index.ts

@@ -1,7 +1,6 @@
 import type { IUser } from '@growi/core';
 import type { IUser } from '@growi/core';
 import { OptionParser } from '@growi/core/dist/remark-plugins';
 import { OptionParser } from '@growi/core/dist/remark-plugins';
 import { pathUtils } from '@growi/core/dist/utils';
 import { pathUtils } from '@growi/core/dist/utils';
-import escapeStringRegexp from 'escape-string-regexp';
 import type { Request, Response } from 'express';
 import type { Request, Response } from 'express';
 import createError, { isHttpError } from 'http-errors';
 import createError, { isHttpError } from 'http-errors';
 
 
@@ -31,16 +30,16 @@ export function addFilterCondition(
     );
     );
   }
   }
 
 
-  const pagePathForRegexp = escapeStringRegexp(addTrailingSlash(pagePath));
+  const pagePathForRegexp = RegExp.escape(addTrailingSlash(pagePath));
 
 
   let filterPath: RegExp;
   let filterPath: RegExp;
   try {
   try {
     if (optionsFilter.charAt(0) === '^') {
     if (optionsFilter.charAt(0) === '^') {
       // move '^' to the first of path
       // move '^' to the first of path
-      const escapedFilter = escapeStringRegexp(optionsFilter.slice(1));
+      const escapedFilter = RegExp.escape(optionsFilter.slice(1));
       filterPath = new RegExp(`^${pagePathForRegexp}${escapedFilter}`);
       filterPath = new RegExp(`^${pagePathForRegexp}${escapedFilter}`);
     } else {
     } else {
-      const escapedFilter = escapeStringRegexp(optionsFilter);
+      const escapedFilter = RegExp.escape(optionsFilter);
       filterPath = new RegExp(`^${pagePathForRegexp}.*${escapedFilter}`);
       filterPath = new RegExp(`^${pagePathForRegexp}.*${escapedFilter}`);
     }
     }
   } catch (err) {
   } catch (err) {
@@ -100,7 +99,7 @@ export const listPages = ({
       if (excludedPaths.length > 0) {
       if (excludedPaths.length > 0) {
         const escapedPaths = excludedPaths.map((p) => {
         const escapedPaths = excludedPaths.map((p) => {
           const cleanPath = p.startsWith('/') ? p.substring(1) : p;
           const cleanPath = p.startsWith('/') ? p.substring(1) : p;
-          return escapeStringRegexp(cleanPath);
+          return RegExp.escape(cleanPath);
         });
         });
 
 
         const regex = new RegExp(`^\\/(${escapedPaths.join('|')})(\\/|$)`);
         const regex = new RegExp(`^\\/(${escapedPaths.join('|')})(\\/|$)`);

File diff suppressed because it is too large
+ 286 - 287
pnpm-lock.yaml


Some files were not shown because too many files changed in this diff