@oalacea/daemon 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -23
- package/README.md +147 -141
- package/bin/Dockerfile +75 -74
- package/dist/cli/cli.d.ts +42 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +89 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/commands/detect.command.d.ts +39 -0
- package/dist/cli/commands/detect.command.d.ts.map +1 -0
- package/dist/cli/commands/detect.command.js +111 -0
- package/dist/cli/commands/detect.command.js.map +1 -0
- package/dist/cli/commands/index.d.ts +14 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +11 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/init.command.d.ts +41 -0
- package/dist/cli/commands/init.command.d.ts.map +1 -0
- package/dist/cli/commands/init.command.js +111 -0
- package/dist/cli/commands/init.command.js.map +1 -0
- package/dist/cli/commands/test.command.d.ts +58 -0
- package/dist/cli/commands/test.command.d.ts.map +1 -0
- package/dist/cli/commands/test.command.js +180 -0
- package/dist/cli/commands/test.command.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +10 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/config/daemon.config.d.ts +32 -0
- package/dist/core/config/daemon.config.d.ts.map +1 -0
- package/dist/core/config/daemon.config.js +83 -0
- package/dist/core/config/daemon.config.js.map +1 -0
- package/dist/core/config/index.d.ts +5 -0
- package/dist/core/config/index.d.ts.map +1 -0
- package/dist/core/config/index.js +5 -0
- package/dist/core/config/index.js.map +1 -0
- package/dist/core/constants.d.ts +36 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/constants.js +56 -0
- package/dist/core/constants.js.map +1 -0
- package/dist/core/types/common.types.d.ts +250 -0
- package/dist/core/types/common.types.d.ts.map +1 -0
- package/dist/core/types/common.types.js +7 -0
- package/dist/core/types/common.types.js.map +1 -0
- package/dist/core/types/detection.types.d.ts +232 -0
- package/dist/core/types/detection.types.d.ts.map +1 -0
- package/dist/core/types/detection.types.js +22 -0
- package/dist/core/types/detection.types.js.map +1 -0
- package/dist/core/types/docker.types.d.ts +322 -0
- package/dist/core/types/docker.types.d.ts.map +1 -0
- package/dist/core/types/docker.types.js +7 -0
- package/dist/core/types/docker.types.js.map +1 -0
- package/dist/core/types/index.d.ts +11 -0
- package/dist/core/types/index.d.ts.map +1 -0
- package/dist/core/types/index.js +7 -0
- package/dist/core/types/index.js.map +1 -0
- package/dist/core/types/project.types.d.ts +74 -0
- package/dist/core/types/project.types.d.ts.map +1 -0
- package/dist/core/types/project.types.js +7 -0
- package/dist/core/types/project.types.js.map +1 -0
- package/dist/core/types/shared.types.d.ts +118 -0
- package/dist/core/types/shared.types.d.ts.map +1 -0
- package/dist/core/types/shared.types.js +7 -0
- package/dist/core/types/shared.types.js.map +1 -0
- package/dist/core/types/test.types.d.ts +230 -0
- package/dist/core/types/test.types.d.ts.map +1 -0
- package/dist/core/types/test.types.js +7 -0
- package/dist/core/types/test.types.js.map +1 -0
- package/dist/services/detection/__tests__/framework-detector.test.d.ts +5 -0
- package/dist/services/detection/__tests__/framework-detector.test.d.ts.map +1 -0
- package/dist/services/detection/__tests__/framework-detector.test.js +52 -0
- package/dist/services/detection/__tests__/framework-detector.test.js.map +1 -0
- package/dist/services/detection/framework-detector.d.ts +179 -0
- package/dist/services/detection/framework-detector.d.ts.map +1 -0
- package/dist/services/detection/framework-detector.js +636 -0
- package/dist/services/detection/framework-detector.js.map +1 -0
- package/dist/services/detection/index.d.ts +10 -0
- package/dist/services/detection/index.d.ts.map +1 -0
- package/dist/services/detection/index.js +7 -0
- package/dist/services/detection/index.js.map +1 -0
- package/dist/services/docker/__tests__/docker-manager.test.d.ts +5 -0
- package/dist/services/docker/__tests__/docker-manager.test.d.ts.map +1 -0
- package/dist/services/docker/__tests__/docker-manager.test.js +67 -0
- package/dist/services/docker/__tests__/docker-manager.test.js.map +1 -0
- package/dist/services/docker/docker-manager.d.ts +157 -0
- package/dist/services/docker/docker-manager.d.ts.map +1 -0
- package/dist/services/docker/docker-manager.js +516 -0
- package/dist/services/docker/docker-manager.js.map +1 -0
- package/dist/services/docker/index.d.ts +9 -0
- package/dist/services/docker/index.d.ts.map +1 -0
- package/dist/services/docker/index.js +9 -0
- package/dist/services/docker/index.js.map +1 -0
- package/dist/services/index.d.ts +10 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +8 -0
- package/dist/services/index.js.map +1 -0
- package/dist/shared/errors/__tests__/base.error.test.d.ts +5 -0
- package/dist/shared/errors/__tests__/base.error.test.d.ts.map +1 -0
- package/dist/shared/errors/__tests__/base.error.test.js +61 -0
- package/dist/shared/errors/__tests__/base.error.test.js.map +1 -0
- package/dist/shared/errors/__tests__/command.error.test.d.ts +5 -0
- package/dist/shared/errors/__tests__/command.error.test.d.ts.map +1 -0
- package/dist/shared/errors/__tests__/command.error.test.js +62 -0
- package/dist/shared/errors/__tests__/command.error.test.js.map +1 -0
- package/dist/shared/errors/__tests__/file.error.test.d.ts +5 -0
- package/dist/shared/errors/__tests__/file.error.test.d.ts.map +1 -0
- package/dist/shared/errors/__tests__/file.error.test.js +75 -0
- package/dist/shared/errors/__tests__/file.error.test.js.map +1 -0
- package/dist/shared/errors/__tests__/index.test.d.ts +5 -0
- package/dist/shared/errors/__tests__/index.test.d.ts.map +1 -0
- package/dist/shared/errors/__tests__/index.test.js +62 -0
- package/dist/shared/errors/__tests__/index.test.js.map +1 -0
- package/dist/shared/errors/__tests__/validation.error.test.d.ts +5 -0
- package/dist/shared/errors/__tests__/validation.error.test.d.ts.map +1 -0
- package/dist/shared/errors/__tests__/validation.error.test.js +79 -0
- package/dist/shared/errors/__tests__/validation.error.test.js.map +1 -0
- package/dist/shared/errors/base.error.d.ts +54 -0
- package/dist/shared/errors/base.error.d.ts.map +1 -0
- package/dist/shared/errors/base.error.js +85 -0
- package/dist/shared/errors/base.error.js.map +1 -0
- package/dist/shared/errors/command.error.d.ts +58 -0
- package/dist/shared/errors/command.error.d.ts.map +1 -0
- package/dist/shared/errors/command.error.js +102 -0
- package/dist/shared/errors/command.error.js.map +1 -0
- package/dist/shared/errors/detection.error.d.ts +42 -0
- package/dist/shared/errors/detection.error.d.ts.map +1 -0
- package/dist/shared/errors/detection.error.js +82 -0
- package/dist/shared/errors/detection.error.js.map +1 -0
- package/dist/shared/errors/docker.error.d.ts +142 -0
- package/dist/shared/errors/docker.error.d.ts.map +1 -0
- package/dist/shared/errors/docker.error.js +172 -0
- package/dist/shared/errors/docker.error.js.map +1 -0
- package/dist/shared/errors/file.error.d.ts +66 -0
- package/dist/shared/errors/file.error.d.ts.map +1 -0
- package/dist/shared/errors/file.error.js +93 -0
- package/dist/shared/errors/file.error.js.map +1 -0
- package/dist/shared/errors/index.d.ts +56 -0
- package/dist/shared/errors/index.d.ts.map +1 -0
- package/dist/shared/errors/index.js +86 -0
- package/dist/shared/errors/index.js.map +1 -0
- package/dist/shared/errors/validation.error.d.ts +67 -0
- package/dist/shared/errors/validation.error.d.ts.map +1 -0
- package/dist/shared/errors/validation.error.js +97 -0
- package/dist/shared/errors/validation.error.js.map +1 -0
- package/dist/shared/templates/index.d.ts +2 -0
- package/dist/shared/templates/index.d.ts.map +1 -0
- package/dist/shared/templates/index.js +2 -0
- package/dist/shared/templates/index.js.map +1 -0
- package/dist/shared/templates/prompt-builder.d.ts +2 -0
- package/dist/shared/templates/prompt-builder.d.ts.map +1 -0
- package/dist/shared/templates/prompt-builder.js +2 -0
- package/dist/shared/templates/prompt-builder.js.map +1 -0
- package/dist/shared/templates/template-engine.d.ts +2 -0
- package/dist/shared/templates/template-engine.d.ts.map +1 -0
- package/dist/shared/templates/template-engine.js +2 -0
- package/dist/shared/templates/template-engine.js.map +1 -0
- package/dist/shared/utils/__tests__/command-executor.test.d.ts +5 -0
- package/dist/shared/utils/__tests__/command-executor.test.d.ts.map +1 -0
- package/dist/shared/utils/__tests__/command-executor.test.js +45 -0
- package/dist/shared/utils/__tests__/command-executor.test.js.map +1 -0
- package/dist/shared/utils/__tests__/file-helper.test.d.ts +5 -0
- package/dist/shared/utils/__tests__/file-helper.test.d.ts.map +1 -0
- package/dist/shared/utils/__tests__/file-helper.test.js +71 -0
- package/dist/shared/utils/__tests__/file-helper.test.js.map +1 -0
- package/dist/shared/utils/__tests__/logger.test.d.ts +5 -0
- package/dist/shared/utils/__tests__/logger.test.d.ts.map +1 -0
- package/dist/shared/utils/__tests__/logger.test.js +83 -0
- package/dist/shared/utils/__tests__/logger.test.js.map +1 -0
- package/dist/shared/utils/command-executer.d.ts +2 -0
- package/dist/shared/utils/command-executer.d.ts.map +1 -0
- package/dist/shared/utils/command-executer.js +2 -0
- package/dist/shared/utils/command-executer.js.map +1 -0
- package/dist/shared/utils/command-executor.d.ts +255 -0
- package/dist/shared/utils/command-executor.d.ts.map +1 -0
- package/dist/shared/utils/command-executor.js +287 -0
- package/dist/shared/utils/command-executor.js.map +1 -0
- package/dist/shared/utils/file-helper.d.ts +86 -0
- package/dist/shared/utils/file-helper.d.ts.map +1 -0
- package/dist/shared/utils/file-helper.js +323 -0
- package/dist/shared/utils/file-helper.js.map +1 -0
- package/dist/shared/utils/index.d.ts +9 -0
- package/dist/shared/utils/index.d.ts.map +1 -0
- package/dist/shared/utils/index.js +9 -0
- package/dist/shared/utils/index.js.map +1 -0
- package/dist/shared/utils/logger.d.ts +163 -0
- package/dist/shared/utils/logger.d.ts.map +1 -0
- package/dist/shared/utils/logger.js +389 -0
- package/dist/shared/utils/logger.js.map +1 -0
- package/package.json +53 -34
- package/prompts/DEPS_EFFICIENCY.md +558 -558
- package/prompts/E2E.md +491 -491
- package/prompts/EXECUTE.md +1060 -1060
- package/prompts/INTEGRATION_API.md +484 -484
- package/prompts/INTEGRATION_DB.md +425 -425
- package/prompts/PERF_API.md +433 -433
- package/prompts/PERF_DB.md +430 -430
- package/prompts/PERF_FRONT.md +357 -357
- package/prompts/REMEDIATION.md +482 -482
- package/prompts/UNIT.md +260 -260
- package/templates/README.md +38 -38
- package/templates/k6/load-test.js +54 -54
- package/templates/playwright/e2e.spec.ts +61 -61
- package/templates/vitest/angular-component.test.ts +38 -38
- package/templates/vitest/api.test.ts +51 -51
- package/templates/vitest/component.test.ts +27 -27
- package/templates/vitest/hook.test.ts +36 -36
- package/templates/vitest/solid-component.test.ts +34 -34
- package/templates/vitest/svelte-component.test.ts +33 -33
- package/templates/vitest/vue-component.test.ts +39 -39
- package/CHANGELOG.md +0 -38
- package/agents/deps-analyzer.js +0 -366
- package/agents/detector.js +0 -570
- package/agents/fix-engine.js +0 -305
- package/agents/lighthouse-scanner.js +0 -405
- package/agents/perf-analyzer.js +0 -294
- package/agents/perf-front-analyzer.js +0 -229
- package/agents/test-generator.js +0 -387
- package/agents/test-runner.js +0 -318
- package/bin/cli.js +0 -449
- package/lib/config.js +0 -250
- package/lib/docker.js +0 -207
- package/lib/reporter.js +0 -297
- package/scripts/dev.js +0 -106
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/svelte';
|
|
2
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
-
|
|
4
|
-
// TODO: Import your component
|
|
5
|
-
// import ComponentName from '@/components/ComponentName.svelte';
|
|
6
|
-
|
|
7
|
-
describe('ComponentName', () => {
|
|
8
|
-
it('should render', () => {
|
|
9
|
-
// TODO: Add component render
|
|
10
|
-
// render(ComponentName);
|
|
11
|
-
// expect(container).toBeTruthy();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('should render with props', () => {
|
|
15
|
-
// TODO: Test props
|
|
16
|
-
// const { container } = render(ComponentName, { props: { title: 'Test Title' } });
|
|
17
|
-
// expect(container.textContent).toContain('Test Title');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should handle user interaction', async () => {
|
|
21
|
-
// TODO: Test events
|
|
22
|
-
// const { container, component } = render(ComponentName);
|
|
23
|
-
// const button = container.querySelector('button');
|
|
24
|
-
// button.click();
|
|
25
|
-
// expect(component.$$).toBeDefined();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should update when props change', async () => {
|
|
29
|
-
// TODO: Test reactivity
|
|
30
|
-
// const { container } = render(ComponentName, { props: { count: 0 } });
|
|
31
|
-
// // Update props and check reactivity
|
|
32
|
-
});
|
|
33
|
-
});
|
|
1
|
+
import { render, screen } from '@testing-library/svelte';
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
+
|
|
4
|
+
// TODO: Import your component
|
|
5
|
+
// import ComponentName from '@/components/ComponentName.svelte';
|
|
6
|
+
|
|
7
|
+
describe('ComponentName', () => {
|
|
8
|
+
it('should render', () => {
|
|
9
|
+
// TODO: Add component render
|
|
10
|
+
// render(ComponentName);
|
|
11
|
+
// expect(container).toBeTruthy();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should render with props', () => {
|
|
15
|
+
// TODO: Test props
|
|
16
|
+
// const { container } = render(ComponentName, { props: { title: 'Test Title' } });
|
|
17
|
+
// expect(container.textContent).toContain('Test Title');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should handle user interaction', async () => {
|
|
21
|
+
// TODO: Test events
|
|
22
|
+
// const { container, component } = render(ComponentName);
|
|
23
|
+
// const button = container.querySelector('button');
|
|
24
|
+
// button.click();
|
|
25
|
+
// expect(component.$$).toBeDefined();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should update when props change', async () => {
|
|
29
|
+
// TODO: Test reactivity
|
|
30
|
+
// const { container } = render(ComponentName, { props: { count: 0 } });
|
|
31
|
+
// // Update props and check reactivity
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import { mount } from '@vue/test-utils';
|
|
2
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
-
|
|
4
|
-
// TODO: Import your component
|
|
5
|
-
// import ComponentName from '@/components/ComponentName.vue';
|
|
6
|
-
|
|
7
|
-
describe('ComponentName', () => {
|
|
8
|
-
it('should render', () => {
|
|
9
|
-
// TODO: Add component render
|
|
10
|
-
// const wrapper = mount(ComponentName);
|
|
11
|
-
// expect(wrapper.exists()).toBe(true);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('should render with props', () => {
|
|
15
|
-
// TODO: Test props
|
|
16
|
-
// const wrapper = mount(ComponentName, {
|
|
17
|
-
// props: { title: 'Test Title' }
|
|
18
|
-
// });
|
|
19
|
-
// expect(wrapper.text()).toContain('Test Title');
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('should handle user interaction', async () => {
|
|
23
|
-
// TODO: Test events
|
|
24
|
-
// const wrapper = mount(ComponentName, {
|
|
25
|
-
// props: { onClick: vi.fn() }
|
|
26
|
-
// });
|
|
27
|
-
// await wrapper.find('button').trigger('click');
|
|
28
|
-
// expect(wrapper.emitted('click')).toBeTruthy();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('should update when props change', async () => {
|
|
32
|
-
// TODO: Test reactivity
|
|
33
|
-
// const wrapper = mount(ComponentName, {
|
|
34
|
-
// props: { count: 0 }
|
|
35
|
-
// });
|
|
36
|
-
// await wrapper.setProps({ count: 5 });
|
|
37
|
-
// expect(wrapper.text()).toContain('5');
|
|
38
|
-
});
|
|
39
|
-
});
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
+
|
|
4
|
+
// TODO: Import your component
|
|
5
|
+
// import ComponentName from '@/components/ComponentName.vue';
|
|
6
|
+
|
|
7
|
+
describe('ComponentName', () => {
|
|
8
|
+
it('should render', () => {
|
|
9
|
+
// TODO: Add component render
|
|
10
|
+
// const wrapper = mount(ComponentName);
|
|
11
|
+
// expect(wrapper.exists()).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should render with props', () => {
|
|
15
|
+
// TODO: Test props
|
|
16
|
+
// const wrapper = mount(ComponentName, {
|
|
17
|
+
// props: { title: 'Test Title' }
|
|
18
|
+
// });
|
|
19
|
+
// expect(wrapper.text()).toContain('Test Title');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should handle user interaction', async () => {
|
|
23
|
+
// TODO: Test events
|
|
24
|
+
// const wrapper = mount(ComponentName, {
|
|
25
|
+
// props: { onClick: vi.fn() }
|
|
26
|
+
// });
|
|
27
|
+
// await wrapper.find('button').trigger('click');
|
|
28
|
+
// expect(wrapper.emitted('click')).toBeTruthy();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should update when props change', async () => {
|
|
32
|
+
// TODO: Test reactivity
|
|
33
|
+
// const wrapper = mount(ComponentName, {
|
|
34
|
+
// props: { count: 0 }
|
|
35
|
+
// });
|
|
36
|
+
// await wrapper.setProps({ count: 5 });
|
|
37
|
+
// expect(wrapper.text()).toContain('5');
|
|
38
|
+
});
|
|
39
|
+
});
|
package/CHANGELOG.md
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Daemon - Changelog
|
|
2
|
-
|
|
3
|
-
## [1.0.0-alpha] - 2025-02-07
|
|
4
|
-
|
|
5
|
-
### Added
|
|
6
|
-
- Initial release
|
|
7
|
-
- Auto-detection for frameworks (Next.js, Remix, SvelteKit, Vite, etc.)
|
|
8
|
-
- Database detection (Prisma, Drizzle, Neon, Supabase, local Postgres)
|
|
9
|
-
- Unit test generation (components, hooks, utils)
|
|
10
|
-
- Integration test templates (API routes, DB with transaction rollback)
|
|
11
|
-
- E2E test templates (Playwright)
|
|
12
|
-
- Performance test templates (k6)
|
|
13
|
-
- Dependency efficiency analysis (TanStack Router, React Query, Prisma, Zustand, React Compiler)
|
|
14
|
-
- Docker container with all testing tools
|
|
15
|
-
- Cross-platform support (Linux, macOS, Windows)
|
|
16
|
-
|
|
17
|
-
### Features
|
|
18
|
-
- **Unit Tests**: Component, hook, and utility testing with Vitest
|
|
19
|
-
- **Integration Tests**: API route and database testing with Prisma
|
|
20
|
-
- **E2E Tests**: User flow testing with Playwright
|
|
21
|
-
- **Performance Tests**: API load testing with k6
|
|
22
|
-
- **Analysis**: Dependency pattern analysis and recommendations
|
|
23
|
-
- **Remediation**: Automatic test failure categorization and fix suggestions
|
|
24
|
-
|
|
25
|
-
### Tools Included
|
|
26
|
-
- Vitest
|
|
27
|
-
- @testing-library/react
|
|
28
|
-
- Playwright
|
|
29
|
-
- k6
|
|
30
|
-
- supertest
|
|
31
|
-
- MSW
|
|
32
|
-
- @prisma/cli
|
|
33
|
-
|
|
34
|
-
### Documentation
|
|
35
|
-
- Comprehensive prompt system for AI agent
|
|
36
|
-
- Test templates for all test types
|
|
37
|
-
- Fix engine for common test failures
|
|
38
|
-
- Performance analysis and reporting
|
package/agents/deps-analyzer.js
DELETED
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Daemon - Dependency Efficiency Analyzer
|
|
3
|
-
*
|
|
4
|
-
* Analyzes codebase for dependency usage patterns and inefficiencies:
|
|
5
|
-
* - TanStack Router patterns
|
|
6
|
-
* - React Query usage
|
|
7
|
-
* - Prisma query patterns
|
|
8
|
-
* - Zustand store patterns
|
|
9
|
-
* - React Compiler readiness
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const fs = require('fs');
|
|
13
|
-
const path = require('path');
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Find files matching a pattern
|
|
17
|
-
*/
|
|
18
|
-
function findFiles(dir, pattern, excludeDirs = ['node_modules', '.next', 'dist', 'build']) {
|
|
19
|
-
const files = [];
|
|
20
|
-
|
|
21
|
-
function traverse(currentDir) {
|
|
22
|
-
if (!fs.existsSync(currentDir)) return;
|
|
23
|
-
|
|
24
|
-
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
25
|
-
|
|
26
|
-
for (const entry of entries) {
|
|
27
|
-
if (entry.isDirectory()) {
|
|
28
|
-
if (excludeDirs.includes(entry.name)) continue;
|
|
29
|
-
traverse(path.join(currentDir, entry.name));
|
|
30
|
-
} else if (entry.isFile() && entry.name.match(pattern)) {
|
|
31
|
-
files.push(path.join(currentDir, entry.name));
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
traverse(dir);
|
|
37
|
-
return files;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Read file content
|
|
42
|
-
*/
|
|
43
|
-
function readFile(filePath) {
|
|
44
|
-
try {
|
|
45
|
-
return fs.readFileSync(filePath, 'utf-8');
|
|
46
|
-
} catch {
|
|
47
|
-
return '';
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Analyze TanStack Router usage
|
|
53
|
-
*/
|
|
54
|
-
function analyzeTanStackRouter(projectDir) {
|
|
55
|
-
const findings = {
|
|
56
|
-
good: [],
|
|
57
|
-
issues: [],
|
|
58
|
-
recommendations: [],
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const routeFiles = findFiles(path.join(projectDir, 'src'), /routes/);
|
|
62
|
-
|
|
63
|
-
for (const file of routeFiles) {
|
|
64
|
-
const content = readFile(file);
|
|
65
|
-
|
|
66
|
-
// Check for typed routes
|
|
67
|
-
if (content.includes('useParams') || content.includes('$')) {
|
|
68
|
-
findings.good.push(`Typed params in ${path.relative(projectDir, file)}`);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Check for loaders
|
|
72
|
-
if (content.includes('loader:') || content.includes('loaderBefore')) {
|
|
73
|
-
findings.good.push(`Data loader in ${path.relative(projectDir, file)}`);
|
|
74
|
-
} else if (content.includes('useQuery') || content.includes('useFetch')) {
|
|
75
|
-
findings.issues.push(`Missing loader in ${path.relative(projectDir, file)} - data fetching without loader`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Check for error boundaries
|
|
79
|
-
if (content.includes('loader:') && !content.includes('errorComponent') && !content.includes('ErrorBoundary')) {
|
|
80
|
-
findings.issues.push(`Missing error boundary in ${path.relative(projectDir, file)}`);
|
|
81
|
-
findings.recommendations.push(`Add errorComponent to route in ${path.relative(projectDir, file)}`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Check navigation for prefetching
|
|
86
|
-
const componentFiles = findFiles(path.join(projectDir, 'src'), /.*\.(tsx|jsx)$/);
|
|
87
|
-
for (const file of componentFiles) {
|
|
88
|
-
const content = readFile(file);
|
|
89
|
-
if (content.includes('<Link') && !content.includes('prefetch=') && !content.includes('prefetchIntent')) {
|
|
90
|
-
findings.issues.push(`Link prefetching not enabled in ${path.relative(projectDir, file)}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return findings;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Analyze React Query usage
|
|
99
|
-
*/
|
|
100
|
-
function analyzeReactQuery(projectDir) {
|
|
101
|
-
const findings = {
|
|
102
|
-
good: [],
|
|
103
|
-
issues: [],
|
|
104
|
-
recommendations: [],
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const hookFiles = findFiles(path.join(projectDir, 'src'), /hooks/);
|
|
108
|
-
const componentFiles = findFiles(path.join(projectDir, 'src'), /.*\.(tsx|jsx)$/);
|
|
109
|
-
const allFiles = [...hookFiles, ...componentFiles];
|
|
110
|
-
|
|
111
|
-
for (const file of allFiles) {
|
|
112
|
-
const content = readFile(file);
|
|
113
|
-
|
|
114
|
-
if (!content.includes('useQuery') && !content.includes('useMutation')) continue;
|
|
115
|
-
|
|
116
|
-
// Check for array cache keys
|
|
117
|
-
if (content.includes('useQuery(') || content.includes('useInfiniteQuery(')) {
|
|
118
|
-
const hasArrayKey = /\['/.test(content) || /queryKey:\s*\[/.test(content);
|
|
119
|
-
if (hasArrayKey) {
|
|
120
|
-
findings.good.push(`Array-based cache keys in ${path.relative(projectDir, file)}`);
|
|
121
|
-
} else {
|
|
122
|
-
findings.issues.push(`Non-array cache keys in ${path.relative(projectDir, file)}`);
|
|
123
|
-
findings.recommendations.push(`Use array-based cache keys in ${path.relative(projectDir, file)}`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Check for staleTime
|
|
128
|
-
if (content.includes('useQuery(') && !content.includes('staleTime')) {
|
|
129
|
-
findings.issues.push(`Missing staleTime in ${path.relative(projectDir, file)}`);
|
|
130
|
-
findings.recommendations.push(`Add staleTime to queries in ${path.relative(projectDir, file)}`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Check for mutation invalidation
|
|
134
|
-
if (content.includes('useMutation(')) {
|
|
135
|
-
if (content.includes('invalidateQueries')) {
|
|
136
|
-
findings.good.push(`Proper invalidation in ${path.relative(projectDir, file)}`);
|
|
137
|
-
} else {
|
|
138
|
-
findings.issues.push(`Missing invalidation in mutation at ${path.relative(projectDir, file)}`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return findings;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Analyze Prisma usage
|
|
148
|
-
*/
|
|
149
|
-
function analyzePrisma(projectDir) {
|
|
150
|
-
const findings = {
|
|
151
|
-
good: [],
|
|
152
|
-
issues: [],
|
|
153
|
-
recommendations: [],
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const libFiles = findFiles(path.join(projectDir, 'src'), /.*\.(ts|js)$/);
|
|
157
|
-
|
|
158
|
-
for (const file of libFiles) {
|
|
159
|
-
const content = readFile(file);
|
|
160
|
-
|
|
161
|
-
if (!content.includes('prisma.')) continue;
|
|
162
|
-
|
|
163
|
-
// Check for select usage
|
|
164
|
-
if (content.includes('prisma.') && content.includes('findMany')) {
|
|
165
|
-
if (content.includes('select:')) {
|
|
166
|
-
findings.good.push(`Using select in ${path.relative(projectDir, file)}`);
|
|
167
|
-
} else if (content.includes('.findMany().then')) {
|
|
168
|
-
findings.issues.push(`Not using select in ${path.relative(projectDir, file)} - returning full objects`);
|
|
169
|
-
findings.recommendations.push(`Add select to prisma queries in ${path.relative(projectDir, file)}`);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Check for potential N+1
|
|
174
|
-
const lines = content.split('\n');
|
|
175
|
-
for (let i = 0; i < lines.length; i++) {
|
|
176
|
-
if (lines[i].includes('findMany') || lines[i].includes('findFirst')) {
|
|
177
|
-
// Check next 10 lines for forEach with prisma query
|
|
178
|
-
for (let j = i + 1; j < Math.min(i + 10, lines.length); j++) {
|
|
179
|
-
if (lines[j].includes('forEach') && lines[j].includes('prisma.')) {
|
|
180
|
-
findings.issues.push(`Potential N+1 query in ${path.relative(projectDir, file)}:${i + 1}`);
|
|
181
|
-
findings.recommendations.push(`Use include or separate query with WHERE in ${path.relative(projectDir, file)}`);
|
|
182
|
-
break;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Check schema for indexes
|
|
190
|
-
const schemaPath = path.join(projectDir, 'prisma', 'schema.prisma');
|
|
191
|
-
if (fs.existsSync(schemaPath)) {
|
|
192
|
-
const schema = readFile(schemaPath);
|
|
193
|
-
const models = schema.matchAll(/model\s+(\w+)\s*{([^}]+)}/g);
|
|
194
|
-
|
|
195
|
-
for (const modelMatch of models) {
|
|
196
|
-
const modelName = modelMatch[1];
|
|
197
|
-
const body = modelMatch[2];
|
|
198
|
-
|
|
199
|
-
if (body.includes('email') && !body.includes('@@index') && !body.includes('@@unique')) {
|
|
200
|
-
findings.recommendations.push(`Add index on ${modelName}.email for faster lookups`);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return findings;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Analyze Zustand usage
|
|
210
|
-
*/
|
|
211
|
-
function analyzeZustand(projectDir) {
|
|
212
|
-
const findings = {
|
|
213
|
-
good: [],
|
|
214
|
-
issues: [],
|
|
215
|
-
recommendations: [],
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
const storeFiles = findFiles(path.join(projectDir, 'src'), /store|stores/);
|
|
219
|
-
const componentFiles = findFiles(path.join(projectDir, 'src'), /.*\.(tsx|jsx)$/);
|
|
220
|
-
|
|
221
|
-
for (const file of storeFiles) {
|
|
222
|
-
const content = readFile(file);
|
|
223
|
-
|
|
224
|
-
// Check store size
|
|
225
|
-
const lines = content.split('\n').length;
|
|
226
|
-
if (lines > 500) {
|
|
227
|
-
findings.issues.push(`Large store file ${path.relative(projectDir, file)} (${lines} lines)`);
|
|
228
|
-
findings.recommendations.push(`Consider splitting ${path.basename(file)} into multiple stores`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
for (const file of componentFiles) {
|
|
233
|
-
const content = readFile(file);
|
|
234
|
-
|
|
235
|
-
if (!content.includes('useStore')) continue;
|
|
236
|
-
|
|
237
|
-
// Check for full-store subscriptions
|
|
238
|
-
if (content.includes('useStore()') || content.match(/useStore\(\s*state\s*=>\s*state/)) {
|
|
239
|
-
findings.issues.push(`Full-store subscription in ${path.relative(projectDir, file)}`);
|
|
240
|
-
findings.recommendations.push(`Use selectors for specific fields in ${path.relative(projectDir, file)}`);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return findings;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Analyze React Compiler readiness
|
|
249
|
-
*/
|
|
250
|
-
function analyzeReactCompilerReadiness(projectDir) {
|
|
251
|
-
const findings = {
|
|
252
|
-
good: [],
|
|
253
|
-
issues: [],
|
|
254
|
-
recommendations: [],
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
const componentFiles = findFiles(path.join(projectDir, 'src'), /.*\.(tsx|jsx)$/);
|
|
258
|
-
|
|
259
|
-
for (const file of componentFiles) {
|
|
260
|
-
const content = readFile(file);
|
|
261
|
-
|
|
262
|
-
// Check for simple useMemo that can be removed
|
|
263
|
-
const simpleMemo = content.match(/useMemo\(\(\)\s*=>\s*([^,]+),\s*\[[^\]]*\]\)/g);
|
|
264
|
-
if (simpleMemo) {
|
|
265
|
-
for (const memo of simpleMemo) {
|
|
266
|
-
const value = memo.match(/=>\s*(.+),/)?.[1];
|
|
267
|
-
if (value && !value.includes('()') && !value.includes('function')) {
|
|
268
|
-
findings.recommendations.push(`Remove simple useMemo in ${path.basename(file)} - React Compiler will handle this`);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Check for useCallback dependencies
|
|
274
|
-
const useCallbacks = content.matchAll(/useCallback\([^)]+\)/g);
|
|
275
|
-
for (const callback of useCallbacks) {
|
|
276
|
-
const deps = callback[0].match(/\[([^\]]*)\]/)?.[1];
|
|
277
|
-
if (deps && deps.trim() === '') {
|
|
278
|
-
findings.issues.push(`useCallback with empty deps in ${path.basename(file)}`);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Check for large inline objects
|
|
283
|
-
const largeObjects = content.match(/{{[\s\S]{200,}}}/g);
|
|
284
|
-
if (largeObjects) {
|
|
285
|
-
findings.issues.push(`Large inline object in ${path.basename(file)} - move outside component`);
|
|
286
|
-
findings.recommendations.push(`Extract large objects to constants in ${path.basename(file)}`);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
return findings;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Analyze bundle optimization
|
|
295
|
-
*/
|
|
296
|
-
function analyzeBundleOptimization(projectDir) {
|
|
297
|
-
const findings = {
|
|
298
|
-
good: [],
|
|
299
|
-
issues: [],
|
|
300
|
-
recommendations: [],
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
const files = findFiles(path.join(projectDir, 'src'), /.*\.(ts|tsx|js|jsx)$/);
|
|
304
|
-
|
|
305
|
-
for (const file of files) {
|
|
306
|
-
const content = readFile(file);
|
|
307
|
-
|
|
308
|
-
// Check for namespace imports
|
|
309
|
-
if (content.includes('* as ')) {
|
|
310
|
-
findings.issues.push(`Namespace import in ${path.basename(file)}`);
|
|
311
|
-
findings.recommendations.push(`Use named imports for better tree-shaking in ${path.basename(file)}`);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Check for large library imports
|
|
315
|
-
const largeLibs = ['monaco-editor', 'codemirror', 'pdfjs-dist', 'fabric'];
|
|
316
|
-
for (const lib of largeLibs) {
|
|
317
|
-
if (content.includes(`from '${lib}'`) || content.includes(`from "${lib}"`)) {
|
|
318
|
-
if (!content.includes('dynamic(') && !content.includes('React.lazy')) {
|
|
319
|
-
findings.recommendations.push(`Use dynamic import for ${lib} in ${path.basename(file)}`);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// Check package.json for duplicate dependencies
|
|
326
|
-
const pkgPath = path.join(projectDir, 'package.json');
|
|
327
|
-
if (fs.existsSync(pkgPath)) {
|
|
328
|
-
const pkg = JSON.parse(readFile(pkgPath));
|
|
329
|
-
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
330
|
-
|
|
331
|
-
for (const [name, version] of Object.entries(deps)) {
|
|
332
|
-
if (name.startsWith('@types/')) {
|
|
333
|
-
const mainPkg = name.substring(6);
|
|
334
|
-
if (mainPkg in deps) {
|
|
335
|
-
findings.good.push(`Types package for ${mainPkg} found`);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return findings;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Run full analysis
|
|
346
|
-
*/
|
|
347
|
-
function analyze(projectDir) {
|
|
348
|
-
return {
|
|
349
|
-
tanStackRouter: analyzeTanStackRouter(projectDir),
|
|
350
|
-
reactQuery: analyzeReactQuery(projectDir),
|
|
351
|
-
prisma: analyzePrisma(projectDir),
|
|
352
|
-
zustand: analyzeZustand(projectDir),
|
|
353
|
-
reactCompiler: analyzeReactCompilerReadiness(projectDir),
|
|
354
|
-
bundleOptimization: analyzeBundleOptimization(projectDir),
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
module.exports = {
|
|
359
|
-
analyze,
|
|
360
|
-
analyzeTanStackRouter,
|
|
361
|
-
analyzeReactQuery,
|
|
362
|
-
analyzePrisma,
|
|
363
|
-
analyzeZustand,
|
|
364
|
-
analyzeReactCompilerReadiness,
|
|
365
|
-
analyzeBundleOptimization,
|
|
366
|
-
};
|