Просмотр исходного кода

feat: implement isMongoId utility function and add tests

Yuki Takei 1 месяц назад
Родитель
Сommit
2162dbfd8b

+ 2 - 1
.kiro/specs/reduce-modules-loaded/analysis-ledger.md

@@ -25,7 +25,8 @@ Measured via `ChunkModuleStatsPlugin` in `next.config.utils.js`. The `initial` c
 | **Baseline (no Phase 2 changes)** | 8.1 | **2,704** | 4,146 | 6,850 | 10,068 | 2026-02-20 |
 | + MermaidViewer dynamic + date-fns subpath | 8.3 | **2,128** | 4,717 | 6,845 | 10,058 | 2026-02-20 |
 | + date-fns locale subpath imports | 8.4 | **1,630** | 4,717 | 6,347 | 9,062 | 2026-02-20 |
-| + null-loader: i18next-fs-backend, bunyan, bunyan-format | 8.N | **1,572** | 4,720 | 6,292 | 9,007 | 2026-02-20 |
+| + null-loader: i18next-fs-backend, bunyan, bunyan-format | 8.5 | **1,572** | 4,720 | 6,292 | 9,007 | 2026-02-20 |
+| + validator → isMongoId regex in LinkEditModal | 8.6 | **1,572** | 4,608 (-112) | 6,180 (-112) | 8,895 (-112) | 2026-02-20 |
 
 > **Note**: Originally reported baseline was 51.5s, but automated measurement on the same machine consistently shows ~31s. The 51.5s figure may reflect cold cache, different system load, or an earlier codebase state.
 

+ 7 - 0
.kiro/specs/reduce-modules-loaded/tasks.md

@@ -187,6 +187,13 @@ The following loop repeats until the user declares completion:
   - Result: initial: 1,572 (-58, -3.6%) / async-only: 4,720 / total: 6,292 / compiled: 9,007
   - _Requirements: 3.1, 3.2, 6.1_
 
+- [x] 8.6 Loop iteration 4: validator → isMongoId regex replacement in LinkEditModal
+  - Replaced `import validator from 'validator'` with lightweight `isMongoId()` regex utility (`/^[0-9a-f]{24}$/i`)
+  - Created `src/client/util/mongo-id.ts` with `isMongoId()` and `mongo-id.spec.ts` with 8 unit tests (TDD)
+  - Eliminated all 113 `validator` modules from async-only chunks (single usage: `validator.isMongoId()` in LinkEditModal.tsx)
+  - Result: initial: 1,572 (unchanged) / async-only: 4,608 (-112, -2.4%) / total: 6,180 (-112) / compiled: 8,895 (-112)
+  - _Requirements: 4.1, 6.1_
+
 - [ ] 8.N Loop iteration N: (next iteration — measure, analyze, propose, implement)
 
 ## Phase 3: Next.js Version Upgrade Evaluation (Deferred)

+ 2 - 4
apps/app/src/client/components/PageEditor/LinkEditModal/LinkEditModal.tsx

@@ -15,9 +15,9 @@ import {
   Popover,
   PopoverBody,
 } from 'reactstrap';
-import validator from 'validator';
 
 import { apiv3Get } from '~/client/util/apiv3-client';
+import { isMongoId } from '~/client/util/mongo-id';
 import { useCurrentPagePath } from '~/states/page';
 import { usePreviewOptions } from '~/stores/renderer';
 import loggerFactory from '~/utils/logger';
@@ -149,9 +149,7 @@ const LinkEditModalSubstance: React.FC = () => {
     if (path.startsWith('/')) {
       try {
         const pathWithoutFragment = new URL(path, 'http://dummy').pathname;
-        const isPermanentLink = validator.isMongoId(
-          pathWithoutFragment.slice(1),
-        );
+        const isPermanentLink = isMongoId(pathWithoutFragment.slice(1));
         const pageId = isPermanentLink ? pathWithoutFragment.slice(1) : null;
 
         const { data } = await apiv3Get('/page', {

+ 37 - 0
apps/app/src/client/util/mongo-id.spec.ts

@@ -0,0 +1,37 @@
+import { describe, expect, it } from 'vitest';
+
+import { isMongoId } from './mongo-id';
+
+describe('isMongoId', () => {
+  it('should return true for a valid 24-char lowercase hex string', () => {
+    expect(isMongoId('507f1f77bcf86cd799439011')).toBe(true);
+  });
+
+  it('should return true for a valid 24-char uppercase hex string', () => {
+    expect(isMongoId('507F1F77BCF86CD799439011')).toBe(true);
+  });
+
+  it('should return true for mixed-case hex string', () => {
+    expect(isMongoId('507f1F77bcF86cd799439011')).toBe(true);
+  });
+
+  it('should return false for a string shorter than 24 chars', () => {
+    expect(isMongoId('507f1f77bcf86cd79943901')).toBe(false);
+  });
+
+  it('should return false for a string longer than 24 chars', () => {
+    expect(isMongoId('507f1f77bcf86cd7994390111')).toBe(false);
+  });
+
+  it('should return false for a non-hex 24-char string', () => {
+    expect(isMongoId('507f1f77bcf86cd79943901g')).toBe(false);
+  });
+
+  it('should return false for an empty string', () => {
+    expect(isMongoId('')).toBe(false);
+  });
+
+  it('should return false for a path-like string', () => {
+    expect(isMongoId('/Sandbox/test-page')).toBe(false);
+  });
+});

+ 10 - 0
apps/app/src/client/util/mongo-id.ts

@@ -0,0 +1,10 @@
+const MONGO_ID_PATTERN = /^[0-9a-f]{24}$/i;
+
+/**
+ * Check if a string is a valid MongoDB ObjectID (24-char hex string).
+ * Lightweight replacement for validator.isMongoId() to avoid pulling
+ * the entire validator package (113 modules) into the client bundle.
+ */
+export const isMongoId = (value: string): boolean => {
+  return MONGO_ID_PATTERN.test(value);
+};