@sentry/wizard 6.6.1 → 6.8.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/CHANGELOG.md +28 -0
- package/LICENSE +97 -8
- package/dist/bin.js +5 -0
- package/dist/bin.js.map +1 -1
- package/dist/e2e-tests/tests/help-message.test.js +5 -1
- package/dist/e2e-tests/tests/help-message.test.js.map +1 -1
- package/dist/e2e-tests/tests/nextjs-15.test.js +79 -0
- package/dist/e2e-tests/tests/nextjs-15.test.js.map +1 -1
- package/dist/e2e-tests/tests/pnpm-workspace.test.d.ts +1 -0
- package/dist/e2e-tests/tests/pnpm-workspace.test.js +206 -0
- package/dist/e2e-tests/tests/pnpm-workspace.test.js.map +1 -0
- package/dist/e2e-tests/tests/react-router.test.d.ts +1 -0
- package/dist/e2e-tests/tests/react-router.test.js +255 -0
- package/dist/e2e-tests/tests/react-router.test.js.map +1 -0
- package/dist/e2e-tests/utils/index.d.ts +8 -2
- package/dist/e2e-tests/utils/index.js +72 -21
- package/dist/e2e-tests/utils/index.js.map +1 -1
- package/dist/lib/Constants.d.ts +1 -0
- package/dist/lib/Constants.js +5 -0
- package/dist/lib/Constants.js.map +1 -1
- package/dist/src/android/android-wizard.js +8 -1
- package/dist/src/android/android-wizard.js.map +1 -1
- package/dist/src/angular/angular-wizard.js +8 -1
- package/dist/src/angular/angular-wizard.js.map +1 -1
- package/dist/src/apple/apple-wizard.js +8 -1
- package/dist/src/apple/apple-wizard.js.map +1 -1
- package/dist/src/flutter/flutter-wizard.js +8 -1
- package/dist/src/flutter/flutter-wizard.js.map +1 -1
- package/dist/src/nextjs/nextjs-wizard.js +35 -9
- package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
- package/dist/src/nextjs/templates.d.ts +3 -3
- package/dist/src/nextjs/templates.js +18 -7
- package/dist/src/nextjs/templates.js.map +1 -1
- package/dist/src/nuxt/nuxt-wizard.js +8 -1
- package/dist/src/nuxt/nuxt-wizard.js.map +1 -1
- package/dist/src/react-native/react-native-wizard.js +8 -1
- package/dist/src/react-native/react-native-wizard.js.map +1 -1
- package/dist/src/react-router/codemods/client.entry.d.ts +1 -0
- package/dist/src/react-router/codemods/client.entry.js +73 -0
- package/dist/src/react-router/codemods/client.entry.js.map +1 -0
- package/dist/src/react-router/codemods/react-router-config.d.ts +9 -0
- package/dist/src/react-router/codemods/react-router-config.js +178 -0
- package/dist/src/react-router/codemods/react-router-config.js.map +1 -0
- package/dist/src/react-router/codemods/root.d.ts +1 -0
- package/dist/src/react-router/codemods/root.js +171 -0
- package/dist/src/react-router/codemods/root.js.map +1 -0
- package/dist/src/react-router/codemods/routes-config.d.ts +1 -0
- package/dist/src/react-router/codemods/routes-config.js +106 -0
- package/dist/src/react-router/codemods/routes-config.js.map +1 -0
- package/dist/src/react-router/codemods/server-entry.d.ts +4 -0
- package/dist/src/react-router/codemods/server-entry.js +275 -0
- package/dist/src/react-router/codemods/server-entry.js.map +1 -0
- package/dist/src/react-router/codemods/utils.d.ts +2 -0
- package/dist/src/react-router/codemods/utils.js +13 -0
- package/dist/src/react-router/codemods/utils.js.map +1 -0
- package/dist/src/react-router/codemods/vite.d.ts +8 -0
- package/dist/src/react-router/codemods/vite.js +169 -0
- package/dist/src/react-router/codemods/vite.js.map +1 -0
- package/dist/src/react-router/react-router-wizard.d.ts +2 -0
- package/dist/src/react-router/react-router-wizard.js +254 -0
- package/dist/src/react-router/react-router-wizard.js.map +1 -0
- package/dist/src/react-router/sdk-example.d.ts +18 -0
- package/dist/src/react-router/sdk-example.js +306 -0
- package/dist/src/react-router/sdk-example.js.map +1 -0
- package/dist/src/react-router/sdk-setup.d.ts +17 -0
- package/dist/src/react-router/sdk-setup.js +250 -0
- package/dist/src/react-router/sdk-setup.js.map +1 -0
- package/dist/src/react-router/templates.d.ts +11 -0
- package/dist/src/react-router/templates.js +273 -0
- package/dist/src/react-router/templates.js.map +1 -0
- package/dist/src/remix/remix-wizard.js +8 -1
- package/dist/src/remix/remix-wizard.js.map +1 -1
- package/dist/src/run.d.ts +2 -1
- package/dist/src/run.js +6 -0
- package/dist/src/run.js.map +1 -1
- package/dist/src/sourcemaps/sourcemaps-wizard.js +8 -1
- package/dist/src/sourcemaps/sourcemaps-wizard.js.map +1 -1
- package/dist/src/sveltekit/sveltekit-wizard.js +8 -1
- package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
- package/dist/src/utils/ast-utils.d.ts +30 -0
- package/dist/src/utils/ast-utils.js +71 -1
- package/dist/src/utils/ast-utils.js.map +1 -1
- package/dist/src/utils/clack/index.d.ts +5 -2
- package/dist/src/utils/clack/index.js +8 -0
- package/dist/src/utils/clack/index.js.map +1 -1
- package/dist/src/utils/package-json.js +86 -2
- package/dist/src/utils/package-json.js.map +1 -1
- package/dist/src/utils/types.d.ts +9 -0
- package/dist/src/utils/types.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/test/nextjs/templates.test.js +20 -0
- package/dist/test/nextjs/templates.test.js.map +1 -1
- package/dist/test/react-router/codemods/client-entry.test.d.ts +1 -0
- package/dist/test/react-router/codemods/client-entry.test.js +168 -0
- package/dist/test/react-router/codemods/client-entry.test.js.map +1 -0
- package/dist/test/react-router/codemods/react-router-config.test.d.ts +1 -0
- package/dist/test/react-router/codemods/react-router-config.test.js +168 -0
- package/dist/test/react-router/codemods/react-router-config.test.js.map +1 -0
- package/dist/test/react-router/codemods/root.test.d.ts +1 -0
- package/dist/test/react-router/codemods/root.test.js +178 -0
- package/dist/test/react-router/codemods/root.test.js.map +1 -0
- package/dist/test/react-router/codemods/server-entry.test.d.ts +1 -0
- package/dist/test/react-router/codemods/server-entry.test.js +415 -0
- package/dist/test/react-router/codemods/server-entry.test.js.map +1 -0
- package/dist/test/react-router/codemods/vite.test.d.ts +1 -0
- package/dist/test/react-router/codemods/vite.test.js +158 -0
- package/dist/test/react-router/codemods/vite.test.js.map +1 -0
- package/dist/test/react-router/routes-config.test.d.ts +1 -0
- package/dist/test/react-router/routes-config.test.js +156 -0
- package/dist/test/react-router/routes-config.test.js.map +1 -0
- package/dist/test/react-router/sdk-setup.test.d.ts +1 -0
- package/dist/test/react-router/sdk-setup.test.js +411 -0
- package/dist/test/react-router/sdk-setup.test.js.map +1 -0
- package/dist/test/react-router/templates.test.d.ts +1 -0
- package/dist/test/react-router/templates.test.js +220 -0
- package/dist/test/react-router/templates.test.js.map +1 -0
- package/dist/test/utils/package-json.test.d.ts +1 -0
- package/dist/test/utils/package-json.test.js +428 -0
- package/dist/test/utils/package-json.test.js.map +1 -0
- package/package.json +4 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.test.js","sourceRoot":"","sources":["../../../../test/react-router/codemods/vite.test.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';\nimport { instrumentViteConfig } from '../../../src/react-router/codemods/vite';\n\nvi.mock('@clack/prompts', () => ({\n default: {\n log: {\n info: vi.fn(),\n warn: vi.fn(),\n success: vi.fn(),\n },\n },\n}));\nvi.mock('fs', async () => {\n const actual = await vi.importActual<typeof fs>('fs');\n return {\n ...actual,\n existsSync: vi.fn(),\n promises: {\n ...actual.promises,\n readFile: vi.fn(),\n writeFile: vi.fn(),\n },\n };\n});\n\ndescribe('Vite Config Instrumentation', () => {\n const mockCwd = '/mock/project';\n\n beforeEach(() => {\n vi.clearAllMocks();\n vi.spyOn(process, 'cwd').mockReturnValue(mockCwd);\n });\n\n afterEach(() => {\n vi.restoreAllMocks();\n });\n\n describe('instrumentViteConfig', () => {\n it('should throw error if vite config file does not exist', async () => {\n vi.mocked(fs.existsSync).mockReturnValue(false);\n\n await expect(\n instrumentViteConfig('my-org', 'my-project'),\n ).rejects.toThrow('Could not find vite.config.ts or vite.config.js');\n });\n\n it('should detect and skip if Sentry content already exists', async () => {\n const existingConfig = `import { defineConfig } from 'vite';\nimport { sentryReactRouter } from '@sentry/react-router';\n\nexport default defineConfig({\n plugins: [sentryReactRouter({ org: \"my-org\", project: \"my-project\" }, config)]\n});`;\n\n vi.mocked(fs.existsSync).mockReturnValue(true);\n vi.mocked(fs.promises.readFile).mockResolvedValue(existingConfig);\n\n const result = await instrumentViteConfig('my-org', 'my-project');\n\n expect(result.wasConverted).toBe(false);\n });\n\n it('should add sentryReactRouter plugin and convert to function form', async () => {\n const simpleConfig = `import { defineConfig } from 'vite';\n\nexport default defineConfig({\n plugins: []\n});`;\n\n const writtenFiles: Record<string, string> = {};\n\n vi.mocked(fs.existsSync).mockReturnValue(true);\n vi.mocked(fs.promises.readFile).mockResolvedValue(simpleConfig);\n vi.mocked(fs.promises.writeFile).mockImplementation(\n (filePath, content) => {\n writtenFiles[filePath as string] = content as string;\n return Promise.resolve();\n },\n );\n\n const result = await instrumentViteConfig('my-org', 'my-project');\n\n expect(result.wasConverted).toBe(true);\n\n const writtenConfig = Object.values(writtenFiles)[0];\n expect(writtenConfig).toContain('sentryReactRouter');\n expect(writtenConfig).toContain('org: \"my-org\"');\n expect(writtenConfig).toContain('project: \"my-project\"');\n expect(writtenConfig).toContain('config =>');\n });\n\n it('should work with existing function form', async () => {\n const functionConfig = `import { defineConfig } from 'vite';\n\nexport default defineConfig(config => ({\n plugins: []\n}));`;\n\n const writtenFiles: Record<string, string> = {};\n\n vi.mocked(fs.existsSync).mockReturnValue(true);\n vi.mocked(fs.promises.readFile).mockResolvedValue(functionConfig);\n vi.mocked(fs.promises.writeFile).mockImplementation(\n (filePath, content) => {\n writtenFiles[filePath as string] = content as string;\n return Promise.resolve();\n },\n );\n\n const result = await instrumentViteConfig('my-org', 'my-project');\n\n expect(result.wasConverted).toBe(false);\n\n const writtenConfig = Object.values(writtenFiles)[0];\n expect(writtenConfig).toContain('sentryReactRouter');\n expect(writtenConfig).toContain('org: \"my-org\"');\n expect(writtenConfig).toContain('project: \"my-project\"');\n });\n\n it('should prefer vite.config.ts over vite.config.js', async () => {\n const configContent = `import { defineConfig } from 'vite';\nexport default defineConfig({ plugins: [] });`;\n\n const writtenFiles: Record<string, string> = {};\n\n // First call checks for vite.config.ts (returns true), second call validates it exists\n vi.mocked(fs.existsSync).mockReturnValue(true);\n vi.mocked(fs.promises.readFile).mockResolvedValue(configContent);\n vi.mocked(fs.promises.writeFile).mockImplementation(\n (filePath, content) => {\n writtenFiles[filePath as string] = content as string;\n return Promise.resolve();\n },\n );\n\n await instrumentViteConfig('my-org', 'my-project');\n\n // Should write to vite.config.ts, not vite.config.js\n const writtenPath = Object.keys(writtenFiles)[0];\n expect(writtenPath).toContain('vite.config.ts');\n expect(writtenPath).not.toContain('vite.config.js');\n });\n\n it('should work with function expression form', async () => {\n const functionConfig = `import { defineConfig } from 'vite';\n\nexport default defineConfig(function(config) {\n return {\n plugins: []\n };\n});`;\n\n const writtenFiles: Record<string, string> = {};\n\n vi.mocked(fs.existsSync).mockReturnValue(true);\n vi.mocked(fs.promises.readFile).mockResolvedValue(functionConfig);\n vi.mocked(fs.promises.writeFile).mockImplementation(\n (filePath, content) => {\n writtenFiles[filePath as string] = content as string;\n return Promise.resolve();\n },\n );\n\n const result = await instrumentViteConfig('my-org', 'my-project');\n\n expect(result.wasConverted).toBe(false);\n\n const writtenConfig = Object.values(writtenFiles)[0];\n expect(writtenConfig).toContain('sentryReactRouter');\n expect(writtenConfig).toContain('org: \"my-org\"');\n expect(writtenConfig).toContain('project: \"my-project\"');\n });\n });\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
const vitest_1 = require("vitest");
|
|
27
|
+
const fs = __importStar(require("fs"));
|
|
28
|
+
const path = __importStar(require("path"));
|
|
29
|
+
const routes_config_1 = require("../../src/react-router/codemods/routes-config");
|
|
30
|
+
vitest_1.vi.mock('@clack/prompts', () => {
|
|
31
|
+
const mock = {
|
|
32
|
+
log: {
|
|
33
|
+
warn: vitest_1.vi.fn(),
|
|
34
|
+
info: vitest_1.vi.fn(),
|
|
35
|
+
success: vitest_1.vi.fn(),
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
default: mock,
|
|
40
|
+
...mock,
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
(0, vitest_1.describe)('addRoutesToConfig codemod', () => {
|
|
44
|
+
let tmpDir;
|
|
45
|
+
let appDir;
|
|
46
|
+
let routesConfigPath;
|
|
47
|
+
(0, vitest_1.beforeEach)(() => {
|
|
48
|
+
vitest_1.vi.clearAllMocks();
|
|
49
|
+
// Create unique tmp directory for each test
|
|
50
|
+
tmpDir = path.join(__dirname, 'tmp', `test-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`);
|
|
51
|
+
appDir = path.join(tmpDir, 'app');
|
|
52
|
+
routesConfigPath = path.join(appDir, 'routes.ts');
|
|
53
|
+
fs.mkdirSync(appDir, { recursive: true });
|
|
54
|
+
});
|
|
55
|
+
(0, vitest_1.afterEach)(() => {
|
|
56
|
+
// Clean up
|
|
57
|
+
if (fs.existsSync(tmpDir)) {
|
|
58
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
(0, vitest_1.it)('should add routes to existing configuration', async () => {
|
|
62
|
+
// Create a routes.ts file
|
|
63
|
+
const routesContent = `import type { RouteConfig } from "@react-router/dev/routes";
|
|
64
|
+
import { index, route } from "@react-router/dev/routes";
|
|
65
|
+
|
|
66
|
+
export default [
|
|
67
|
+
index("routes/home.tsx"),
|
|
68
|
+
route("/about", "routes/about.tsx"),
|
|
69
|
+
] satisfies RouteConfig;`;
|
|
70
|
+
fs.writeFileSync(routesConfigPath, routesContent);
|
|
71
|
+
await (0, routes_config_1.addRoutesToConfig)(routesConfigPath, true);
|
|
72
|
+
// Check that both routes were added
|
|
73
|
+
const updatedContent = fs.readFileSync(routesConfigPath, 'utf-8');
|
|
74
|
+
(0, vitest_1.expect)(updatedContent).toContain('route("/sentry-example-page", "routes/sentry-example-page.tsx")');
|
|
75
|
+
(0, vitest_1.expect)(updatedContent).toContain('route("/api/sentry-example-api", "routes/api.sentry-example-api.ts")');
|
|
76
|
+
});
|
|
77
|
+
(0, vitest_1.it)('should handle JavaScript projects correctly', async () => {
|
|
78
|
+
// Create a routes.ts file
|
|
79
|
+
const routesContent = `import type { RouteConfig } from "@react-router/dev/routes";
|
|
80
|
+
import { index, route } from "@react-router/dev/routes";
|
|
81
|
+
|
|
82
|
+
export default [
|
|
83
|
+
index("routes/home.jsx"),
|
|
84
|
+
] satisfies RouteConfig;`;
|
|
85
|
+
fs.writeFileSync(routesConfigPath, routesContent);
|
|
86
|
+
await (0, routes_config_1.addRoutesToConfig)(routesConfigPath, false); // JavaScript project
|
|
87
|
+
// Check that both routes were added with .jsx/.js extensions
|
|
88
|
+
const updatedContent = fs.readFileSync(routesConfigPath, 'utf-8');
|
|
89
|
+
(0, vitest_1.expect)(updatedContent).toContain('route("/sentry-example-page", "routes/sentry-example-page.jsx")');
|
|
90
|
+
(0, vitest_1.expect)(updatedContent).toContain('route("/api/sentry-example-api", "routes/api.sentry-example-api.js")');
|
|
91
|
+
});
|
|
92
|
+
(0, vitest_1.it)('should not duplicate routes if they already exist', async () => {
|
|
93
|
+
// Create a routes.ts file with both routes already present
|
|
94
|
+
const routesContent = `import type { RouteConfig } from "@react-router/dev/routes";
|
|
95
|
+
import { index, route } from "@react-router/dev/routes";
|
|
96
|
+
|
|
97
|
+
export default [
|
|
98
|
+
index("routes/home.tsx"),
|
|
99
|
+
route("/sentry-example-page", "routes/sentry-example-page.tsx"),
|
|
100
|
+
route("/api/sentry-example-api", "routes/api.sentry-example-api.ts"),
|
|
101
|
+
] satisfies RouteConfig;`;
|
|
102
|
+
fs.writeFileSync(routesConfigPath, routesContent);
|
|
103
|
+
await (0, routes_config_1.addRoutesToConfig)(routesConfigPath, true);
|
|
104
|
+
// Check that the routes were not duplicated
|
|
105
|
+
const updatedContent = fs.readFileSync(routesConfigPath, 'utf-8');
|
|
106
|
+
const pageRouteMatches = updatedContent.match(/route\("\/sentry-example-page"/g);
|
|
107
|
+
const apiRouteMatches = updatedContent.match(/route\("\/api\/sentry-example-api"/g);
|
|
108
|
+
(0, vitest_1.expect)(pageRouteMatches).toHaveLength(1);
|
|
109
|
+
(0, vitest_1.expect)(apiRouteMatches).toHaveLength(1);
|
|
110
|
+
});
|
|
111
|
+
(0, vitest_1.it)('should add route import when it does not exist', async () => {
|
|
112
|
+
// Create a routes.ts file without route import
|
|
113
|
+
const routesContent = `import type { RouteConfig } from "@react-router/dev/routes";
|
|
114
|
+
import { index } from "@react-router/dev/routes";
|
|
115
|
+
|
|
116
|
+
export default [
|
|
117
|
+
index("routes/home.tsx"),
|
|
118
|
+
] satisfies RouteConfig;`;
|
|
119
|
+
fs.writeFileSync(routesConfigPath, routesContent);
|
|
120
|
+
await (0, routes_config_1.addRoutesToConfig)(routesConfigPath, true);
|
|
121
|
+
// Check that the route import was added
|
|
122
|
+
const updatedContent = fs.readFileSync(routesConfigPath, 'utf-8');
|
|
123
|
+
(0, vitest_1.expect)(updatedContent).toContain('route');
|
|
124
|
+
(0, vitest_1.expect)(updatedContent).toContain('route("/sentry-example-page", "routes/sentry-example-page.tsx")');
|
|
125
|
+
});
|
|
126
|
+
(0, vitest_1.it)('should create default export when it does not exist', async () => {
|
|
127
|
+
// Create a routes.ts file without default export
|
|
128
|
+
const routesContent = `import type { RouteConfig } from "@react-router/dev/routes";
|
|
129
|
+
import { index, route } from "@react-router/dev/routes";`;
|
|
130
|
+
fs.writeFileSync(routesConfigPath, routesContent);
|
|
131
|
+
await (0, routes_config_1.addRoutesToConfig)(routesConfigPath, true);
|
|
132
|
+
// Check that the default export was created
|
|
133
|
+
const updatedContent = fs.readFileSync(routesConfigPath, 'utf-8');
|
|
134
|
+
(0, vitest_1.expect)(updatedContent).toContain('export default [');
|
|
135
|
+
(0, vitest_1.expect)(updatedContent).toContain('route("/sentry-example-page", "routes/sentry-example-page.tsx")');
|
|
136
|
+
(0, vitest_1.expect)(updatedContent).toContain('route("/api/sentry-example-api", "routes/api.sentry-example-api.ts")');
|
|
137
|
+
});
|
|
138
|
+
(0, vitest_1.it)('should handle empty file gracefully', async () => {
|
|
139
|
+
// Create an empty routes.ts file
|
|
140
|
+
fs.writeFileSync(routesConfigPath, '');
|
|
141
|
+
await (0, routes_config_1.addRoutesToConfig)(routesConfigPath, true);
|
|
142
|
+
// Check that everything was added from scratch
|
|
143
|
+
const updatedContent = fs.readFileSync(routesConfigPath, 'utf-8');
|
|
144
|
+
(0, vitest_1.expect)(updatedContent).toContain('import { route } from "@react-router/dev/routes";');
|
|
145
|
+
(0, vitest_1.expect)(updatedContent).toContain('export default [');
|
|
146
|
+
(0, vitest_1.expect)(updatedContent).toContain('route("/sentry-example-page", "routes/sentry-example-page.tsx")');
|
|
147
|
+
(0, vitest_1.expect)(updatedContent).toContain('route("/api/sentry-example-api", "routes/api.sentry-example-api.ts")');
|
|
148
|
+
});
|
|
149
|
+
(0, vitest_1.it)('should skip if file does not exist', async () => {
|
|
150
|
+
// Don't create the file
|
|
151
|
+
await (0, routes_config_1.addRoutesToConfig)(routesConfigPath, true);
|
|
152
|
+
// Should not create the file if it doesn't exist
|
|
153
|
+
(0, vitest_1.expect)(fs.existsSync(routesConfigPath)).toBe(false);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
//# sourceMappingURL=routes-config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes-config.test.js","sourceRoot":"","sources":["../../../test/react-router/routes-config.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,mCAAyE;AACzE,uCAAyB;AACzB,2CAA6B;AAC7B,iFAAkF;AAElF,WAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC7B,MAAM,IAAI,GAAG;QACX,GAAG,EAAE;YACH,IAAI,EAAE,WAAE,CAAC,EAAE,EAAE;YACb,IAAI,EAAE,WAAE,CAAC,EAAE,EAAE;YACb,OAAO,EAAE,WAAE,CAAC,EAAE,EAAE;SACjB;KACF,CAAC;IACF,OAAO;QACL,OAAO,EAAE,IAAI;QACb,GAAG,IAAI;KACR,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAA,iBAAQ,EAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,IAAI,MAAc,CAAC;IACnB,IAAI,MAAc,CAAC;IACnB,IAAI,gBAAwB,CAAC;IAE7B,IAAA,mBAAU,EAAC,GAAG,EAAE;QACd,WAAE,CAAC,aAAa,EAAE,CAAC;QAEnB,4CAA4C;QAC5C,MAAM,GAAG,IAAI,CAAC,IAAI,CAChB,SAAS,EACT,KAAK,EACL,QAAQ,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAChE,CAAC;QACF,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAClC,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAElD,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,IAAA,kBAAS,EAAC,GAAG,EAAE;QACb,WAAW;QACX,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE;YACzB,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;SACrD;IACH,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC3D,0BAA0B;QAC1B,MAAM,aAAa,GAAG;;;;;;yBAMD,CAAC;QAEtB,EAAE,CAAC,aAAa,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;QAElD,MAAM,IAAA,iCAAiB,EAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;QAEhD,oCAAoC;QACpC,MAAM,cAAc,GAAG,EAAE,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAClE,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,SAAS,CAC9B,iEAAiE,CAClE,CAAC;QACF,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,SAAS,CAC9B,sEAAsE,CACvE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC3D,0BAA0B;QAC1B,MAAM,aAAa,GAAG;;;;;yBAKD,CAAC;QAEtB,EAAE,CAAC,aAAa,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;QAElD,MAAM,IAAA,iCAAiB,EAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC,CAAC,qBAAqB;QAEvE,6DAA6D;QAC7D,MAAM,cAAc,GAAG,EAAE,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAClE,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,SAAS,CAC9B,iEAAiE,CAClE,CAAC;QACF,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,SAAS,CAC9B,sEAAsE,CACvE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QACjE,2DAA2D;QAC3D,MAAM,aAAa,GAAG;;;;;;;yBAOD,CAAC;QAEtB,EAAE,CAAC,aAAa,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;QAElD,MAAM,IAAA,iCAAiB,EAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;QAEhD,4CAA4C;QAC5C,MAAM,cAAc,GAAG,EAAE,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAClE,MAAM,gBAAgB,GAAG,cAAc,CAAC,KAAK,CAC3C,iCAAiC,CAClC,CAAC;QACF,MAAM,eAAe,GAAG,cAAc,CAAC,KAAK,CAC1C,qCAAqC,CACtC,CAAC;QACF,IAAA,eAAM,EAAC,gBAAgB,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACzC,IAAA,eAAM,EAAC,eAAe,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,+CAA+C;QAC/C,MAAM,aAAa,GAAG;;;;;yBAKD,CAAC;QAEtB,EAAE,CAAC,aAAa,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;QAElD,MAAM,IAAA,iCAAiB,EAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;QAEhD,wCAAwC;QACxC,MAAM,cAAc,GAAG,EAAE,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAClE,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,SAAS,CAC9B,iEAAiE,CAClE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,iDAAiD;QACjD,MAAM,aAAa,GAAG;yDAC+B,CAAC;QAEtD,EAAE,CAAC,aAAa,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;QAElD,MAAM,IAAA,iCAAiB,EAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;QAEhD,4CAA4C;QAC5C,MAAM,cAAc,GAAG,EAAE,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAClE,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QACrD,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,SAAS,CAC9B,iEAAiE,CAClE,CAAC;QACF,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,SAAS,CAC9B,sEAAsE,CACvE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,iCAAiC;QACjC,EAAE,CAAC,aAAa,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;QAEvC,MAAM,IAAA,iCAAiB,EAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;QAEhD,+CAA+C;QAC/C,MAAM,cAAc,GAAG,EAAE,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAClE,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,SAAS,CAC9B,mDAAmD,CACpD,CAAC;QACF,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QACrD,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,SAAS,CAC9B,iEAAiE,CAClE,CAAC;QACF,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,SAAS,CAC9B,sEAAsE,CACvE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;QAClD,wBAAwB;QACxB,MAAM,IAAA,iCAAiB,EAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;QAEhD,iDAAiD;QACjD,IAAA,eAAM,EAAC,EAAE,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { addRoutesToConfig } from '../../src/react-router/codemods/routes-config';\n\nvi.mock('@clack/prompts', () => {\n const mock = {\n log: {\n warn: vi.fn(),\n info: vi.fn(),\n success: vi.fn(),\n },\n };\n return {\n default: mock,\n ...mock,\n };\n});\n\ndescribe('addRoutesToConfig codemod', () => {\n let tmpDir: string;\n let appDir: string;\n let routesConfigPath: string;\n\n beforeEach(() => {\n vi.clearAllMocks();\n\n // Create unique tmp directory for each test\n tmpDir = path.join(\n __dirname,\n 'tmp',\n `test-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,\n );\n appDir = path.join(tmpDir, 'app');\n routesConfigPath = path.join(appDir, 'routes.ts');\n\n fs.mkdirSync(appDir, { recursive: true });\n });\n\n afterEach(() => {\n // Clean up\n if (fs.existsSync(tmpDir)) {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n }\n });\n\n it('should add routes to existing configuration', async () => {\n // Create a routes.ts file\n const routesContent = `import type { RouteConfig } from \"@react-router/dev/routes\";\nimport { index, route } from \"@react-router/dev/routes\";\n\nexport default [\n index(\"routes/home.tsx\"),\n route(\"/about\", \"routes/about.tsx\"),\n] satisfies RouteConfig;`;\n\n fs.writeFileSync(routesConfigPath, routesContent);\n\n await addRoutesToConfig(routesConfigPath, true);\n\n // Check that both routes were added\n const updatedContent = fs.readFileSync(routesConfigPath, 'utf-8');\n expect(updatedContent).toContain(\n 'route(\"/sentry-example-page\", \"routes/sentry-example-page.tsx\")',\n );\n expect(updatedContent).toContain(\n 'route(\"/api/sentry-example-api\", \"routes/api.sentry-example-api.ts\")',\n );\n });\n\n it('should handle JavaScript projects correctly', async () => {\n // Create a routes.ts file\n const routesContent = `import type { RouteConfig } from \"@react-router/dev/routes\";\nimport { index, route } from \"@react-router/dev/routes\";\n\nexport default [\n index(\"routes/home.jsx\"),\n] satisfies RouteConfig;`;\n\n fs.writeFileSync(routesConfigPath, routesContent);\n\n await addRoutesToConfig(routesConfigPath, false); // JavaScript project\n\n // Check that both routes were added with .jsx/.js extensions\n const updatedContent = fs.readFileSync(routesConfigPath, 'utf-8');\n expect(updatedContent).toContain(\n 'route(\"/sentry-example-page\", \"routes/sentry-example-page.jsx\")',\n );\n expect(updatedContent).toContain(\n 'route(\"/api/sentry-example-api\", \"routes/api.sentry-example-api.js\")',\n );\n });\n\n it('should not duplicate routes if they already exist', async () => {\n // Create a routes.ts file with both routes already present\n const routesContent = `import type { RouteConfig } from \"@react-router/dev/routes\";\nimport { index, route } from \"@react-router/dev/routes\";\n\nexport default [\n index(\"routes/home.tsx\"),\n route(\"/sentry-example-page\", \"routes/sentry-example-page.tsx\"),\n route(\"/api/sentry-example-api\", \"routes/api.sentry-example-api.ts\"),\n] satisfies RouteConfig;`;\n\n fs.writeFileSync(routesConfigPath, routesContent);\n\n await addRoutesToConfig(routesConfigPath, true);\n\n // Check that the routes were not duplicated\n const updatedContent = fs.readFileSync(routesConfigPath, 'utf-8');\n const pageRouteMatches = updatedContent.match(\n /route\\(\"\\/sentry-example-page\"/g,\n );\n const apiRouteMatches = updatedContent.match(\n /route\\(\"\\/api\\/sentry-example-api\"/g,\n );\n expect(pageRouteMatches).toHaveLength(1);\n expect(apiRouteMatches).toHaveLength(1);\n });\n\n it('should add route import when it does not exist', async () => {\n // Create a routes.ts file without route import\n const routesContent = `import type { RouteConfig } from \"@react-router/dev/routes\";\nimport { index } from \"@react-router/dev/routes\";\n\nexport default [\n index(\"routes/home.tsx\"),\n] satisfies RouteConfig;`;\n\n fs.writeFileSync(routesConfigPath, routesContent);\n\n await addRoutesToConfig(routesConfigPath, true);\n\n // Check that the route import was added\n const updatedContent = fs.readFileSync(routesConfigPath, 'utf-8');\n expect(updatedContent).toContain('route');\n expect(updatedContent).toContain(\n 'route(\"/sentry-example-page\", \"routes/sentry-example-page.tsx\")',\n );\n });\n\n it('should create default export when it does not exist', async () => {\n // Create a routes.ts file without default export\n const routesContent = `import type { RouteConfig } from \"@react-router/dev/routes\";\nimport { index, route } from \"@react-router/dev/routes\";`;\n\n fs.writeFileSync(routesConfigPath, routesContent);\n\n await addRoutesToConfig(routesConfigPath, true);\n\n // Check that the default export was created\n const updatedContent = fs.readFileSync(routesConfigPath, 'utf-8');\n expect(updatedContent).toContain('export default [');\n expect(updatedContent).toContain(\n 'route(\"/sentry-example-page\", \"routes/sentry-example-page.tsx\")',\n );\n expect(updatedContent).toContain(\n 'route(\"/api/sentry-example-api\", \"routes/api.sentry-example-api.ts\")',\n );\n });\n\n it('should handle empty file gracefully', async () => {\n // Create an empty routes.ts file\n fs.writeFileSync(routesConfigPath, '');\n\n await addRoutesToConfig(routesConfigPath, true);\n\n // Check that everything was added from scratch\n const updatedContent = fs.readFileSync(routesConfigPath, 'utf-8');\n expect(updatedContent).toContain(\n 'import { route } from \"@react-router/dev/routes\";',\n );\n expect(updatedContent).toContain('export default [');\n expect(updatedContent).toContain(\n 'route(\"/sentry-example-page\", \"routes/sentry-example-page.tsx\")',\n );\n expect(updatedContent).toContain(\n 'route(\"/api/sentry-example-api\", \"routes/api.sentry-example-api.ts\")',\n );\n });\n\n it('should skip if file does not exist', async () => {\n // Don't create the file\n await addRoutesToConfig(routesConfigPath, true);\n\n // Should not create the file if it doesn't exist\n expect(fs.existsSync(routesConfigPath)).toBe(false);\n });\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
const vitest_1 = require("vitest");
|
|
27
|
+
const { clackMocks } = vitest_1.vi.hoisted(() => {
|
|
28
|
+
const info = vitest_1.vi.fn();
|
|
29
|
+
const warn = vitest_1.vi.fn();
|
|
30
|
+
const error = vitest_1.vi.fn();
|
|
31
|
+
const success = vitest_1.vi.fn();
|
|
32
|
+
const outro = vitest_1.vi.fn();
|
|
33
|
+
const confirm = vitest_1.vi.fn(() => Promise.resolve(false)); // default to false for tests
|
|
34
|
+
return {
|
|
35
|
+
clackMocks: {
|
|
36
|
+
info,
|
|
37
|
+
warn,
|
|
38
|
+
error,
|
|
39
|
+
success,
|
|
40
|
+
outro,
|
|
41
|
+
confirm,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
vitest_1.vi.mock('@clack/prompts', () => {
|
|
46
|
+
return {
|
|
47
|
+
__esModule: true,
|
|
48
|
+
default: {
|
|
49
|
+
log: {
|
|
50
|
+
info: clackMocks.info,
|
|
51
|
+
warn: clackMocks.warn,
|
|
52
|
+
error: clackMocks.error,
|
|
53
|
+
success: clackMocks.success,
|
|
54
|
+
},
|
|
55
|
+
outro: clackMocks.outro,
|
|
56
|
+
confirm: clackMocks.confirm,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
const { existsSyncMock, readFileSyncMock, writeFileSyncMock } = vitest_1.vi.hoisted(() => {
|
|
61
|
+
return {
|
|
62
|
+
existsSyncMock: vitest_1.vi.fn(),
|
|
63
|
+
readFileSyncMock: vitest_1.vi.fn(),
|
|
64
|
+
writeFileSyncMock: vitest_1.vi.fn(),
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
const { getPackageDotJsonMock, getPackageVersionMock } = vitest_1.vi.hoisted(() => ({
|
|
68
|
+
getPackageDotJsonMock: vitest_1.vi.fn(),
|
|
69
|
+
getPackageVersionMock: vitest_1.vi.fn(),
|
|
70
|
+
}));
|
|
71
|
+
vitest_1.vi.mock('../../src/utils/package-json', () => ({
|
|
72
|
+
getPackageDotJson: getPackageDotJsonMock,
|
|
73
|
+
getPackageVersion: getPackageVersionMock,
|
|
74
|
+
}));
|
|
75
|
+
vitest_1.vi.mock('fs', async () => {
|
|
76
|
+
return {
|
|
77
|
+
...(await vitest_1.vi.importActual('fs')),
|
|
78
|
+
existsSync: existsSyncMock,
|
|
79
|
+
readFileSync: readFileSyncMock,
|
|
80
|
+
writeFileSync: writeFileSyncMock,
|
|
81
|
+
promises: {
|
|
82
|
+
writeFile: vitest_1.vi.fn(),
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
// module-level mock for child_process.execSync
|
|
87
|
+
vitest_1.vi.mock('child_process', () => ({
|
|
88
|
+
__esModule: true,
|
|
89
|
+
execSync: vitest_1.vi.fn(),
|
|
90
|
+
}));
|
|
91
|
+
// mock showCopyPasteInstructions and makeCodeSnippet used by templates
|
|
92
|
+
vitest_1.vi.mock('../../src/utils/clack', () => {
|
|
93
|
+
return {
|
|
94
|
+
__esModule: true,
|
|
95
|
+
showCopyPasteInstructions: vitest_1.vi.fn(() => Promise.resolve()),
|
|
96
|
+
makeCodeSnippet: vitest_1.vi.fn((colors, callback) => {
|
|
97
|
+
// Mock implementation that just calls the callback with simple string functions
|
|
98
|
+
const unchanged = (str) => str;
|
|
99
|
+
const plus = (str) => `+ ${str}`;
|
|
100
|
+
const minus = (str) => `- ${str}`;
|
|
101
|
+
return callback(unchanged, plus, minus);
|
|
102
|
+
}),
|
|
103
|
+
getPackageDotJson: getPackageDotJsonMock,
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
const sdk_setup_1 = require("../../src/react-router/sdk-setup");
|
|
107
|
+
const childProcess = __importStar(require("child_process"));
|
|
108
|
+
const templates_1 = require("../../src/react-router/templates");
|
|
109
|
+
(0, vitest_1.describe)('React Router SDK Setup', () => {
|
|
110
|
+
(0, vitest_1.beforeEach)(() => {
|
|
111
|
+
vitest_1.vi.clearAllMocks();
|
|
112
|
+
vitest_1.vi.resetAllMocks();
|
|
113
|
+
getPackageVersionMock.mockImplementation((packageName, packageJson) => {
|
|
114
|
+
if (packageJson.dependencies?.[packageName]) {
|
|
115
|
+
return packageJson.dependencies[packageName];
|
|
116
|
+
}
|
|
117
|
+
if (packageJson.devDependencies?.[packageName]) {
|
|
118
|
+
return packageJson.devDependencies[packageName];
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
(0, vitest_1.describe)('isReactRouterV7', () => {
|
|
124
|
+
(0, vitest_1.it)('should return true for React Router v7+ in dependencies or devDependencies', () => {
|
|
125
|
+
(0, vitest_1.expect)((0, sdk_setup_1.isReactRouterV7)({ dependencies: { '@react-router/dev': '7.0.0' } })).toBe(true);
|
|
126
|
+
(0, vitest_1.expect)((0, sdk_setup_1.isReactRouterV7)({ dependencies: { '@react-router/dev': '^7.1.0' } })).toBe(true);
|
|
127
|
+
(0, vitest_1.expect)((0, sdk_setup_1.isReactRouterV7)({
|
|
128
|
+
devDependencies: { '@react-router/dev': '7.1.0' },
|
|
129
|
+
})).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
(0, vitest_1.it)('should return false for React Router v6 or missing dependency', () => {
|
|
132
|
+
(0, vitest_1.expect)((0, sdk_setup_1.isReactRouterV7)({ dependencies: { '@react-router/dev': '6.28.0' } })).toBe(false);
|
|
133
|
+
(0, vitest_1.expect)((0, sdk_setup_1.isReactRouterV7)({ dependencies: { react: '^18.0.0' } })).toBe(false);
|
|
134
|
+
(0, vitest_1.expect)((0, sdk_setup_1.isReactRouterV7)({})).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
(0, vitest_1.describe)('generateServerInstrumentation', () => {
|
|
138
|
+
(0, vitest_1.it)('should generate server instrumentation file with all features enabled', () => {
|
|
139
|
+
const dsn = 'https://sentry.io/123';
|
|
140
|
+
const enableTracing = true;
|
|
141
|
+
const enableProfiling = false;
|
|
142
|
+
const enableLogs = true;
|
|
143
|
+
const result = (0, templates_1.getSentryInstrumentationServerContent)(dsn, enableTracing, enableProfiling, enableLogs);
|
|
144
|
+
(0, vitest_1.expect)(result).toContain('dsn: "https://sentry.io/123"');
|
|
145
|
+
(0, vitest_1.expect)(result).toContain('tracesSampleRate: 1');
|
|
146
|
+
(0, vitest_1.expect)(result).toContain('enableLogs: true');
|
|
147
|
+
});
|
|
148
|
+
(0, vitest_1.it)('should generate server instrumentation file when performance is disabled', () => {
|
|
149
|
+
const dsn = 'https://sentry.io/123';
|
|
150
|
+
const enableTracing = false;
|
|
151
|
+
const enableProfiling = false;
|
|
152
|
+
const enableLogs = false;
|
|
153
|
+
const result = (0, templates_1.getSentryInstrumentationServerContent)(dsn, enableTracing, enableProfiling, enableLogs);
|
|
154
|
+
(0, vitest_1.expect)(result).toContain('dsn: "https://sentry.io/123"');
|
|
155
|
+
(0, vitest_1.expect)(result).toContain('tracesSampleRate: 0');
|
|
156
|
+
(0, vitest_1.expect)(result).not.toContain('enableLogs: true');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
(0, vitest_1.describe)('runReactRouterReveal', () => {
|
|
161
|
+
(0, vitest_1.beforeEach)(() => {
|
|
162
|
+
vitest_1.vi.clearAllMocks();
|
|
163
|
+
vitest_1.vi.resetAllMocks();
|
|
164
|
+
});
|
|
165
|
+
(0, vitest_1.it)('runs the reveal CLI when entry files are missing', () => {
|
|
166
|
+
existsSyncMock.mockReturnValue(false);
|
|
167
|
+
childProcess.execSync.mockImplementation(() => 'ok');
|
|
168
|
+
(0, sdk_setup_1.runReactRouterReveal)(false);
|
|
169
|
+
(0, vitest_1.expect)(childProcess.execSync).toHaveBeenCalledWith('npx react-router reveal', {
|
|
170
|
+
encoding: 'utf8',
|
|
171
|
+
stdio: 'pipe',
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
(0, vitest_1.it)('does not run the reveal CLI when entry files already exist', () => {
|
|
175
|
+
existsSyncMock.mockReturnValue(true);
|
|
176
|
+
childProcess.execSync.mockReset();
|
|
177
|
+
(0, sdk_setup_1.runReactRouterReveal)(false);
|
|
178
|
+
(0, vitest_1.expect)(childProcess.execSync).not.toHaveBeenCalled();
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
(0, vitest_1.describe)('server instrumentation helpers', () => {
|
|
182
|
+
(0, vitest_1.beforeEach)(() => {
|
|
183
|
+
vitest_1.vi.clearAllMocks();
|
|
184
|
+
vitest_1.vi.resetAllMocks();
|
|
185
|
+
});
|
|
186
|
+
(0, vitest_1.it)('createServerInstrumentationFile writes instrumentation file and returns path', () => {
|
|
187
|
+
writeFileSyncMock.mockImplementation(() => undefined);
|
|
188
|
+
const path = (0, sdk_setup_1.createServerInstrumentationFile)('https://sentry.io/123', {
|
|
189
|
+
performance: true,
|
|
190
|
+
replay: false,
|
|
191
|
+
logs: true,
|
|
192
|
+
profiling: false,
|
|
193
|
+
});
|
|
194
|
+
(0, vitest_1.expect)(path).toContain('instrument.server.mjs');
|
|
195
|
+
(0, vitest_1.expect)(writeFileSyncMock).toHaveBeenCalled();
|
|
196
|
+
const writtenCall = writeFileSyncMock.mock.calls[0];
|
|
197
|
+
(0, vitest_1.expect)(writtenCall[0]).toEqual(vitest_1.expect.stringContaining('instrument.server.mjs'));
|
|
198
|
+
(0, vitest_1.expect)(writtenCall[1]).toEqual(vitest_1.expect.stringContaining('dsn: "https://sentry.io/123"'));
|
|
199
|
+
(0, vitest_1.expect)(writtenCall[1]).toEqual(vitest_1.expect.stringContaining('tracesSampleRate: 1'));
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
(0, vitest_1.describe)('tryRevealAndGetManualInstructions', () => {
|
|
203
|
+
(0, vitest_1.beforeEach)(() => {
|
|
204
|
+
vitest_1.vi.clearAllMocks();
|
|
205
|
+
vitest_1.vi.resetAllMocks();
|
|
206
|
+
});
|
|
207
|
+
(0, vitest_1.it)('should return true when user confirms and reveal command succeeds', async () => {
|
|
208
|
+
const missingFilename = 'entry.client.tsx';
|
|
209
|
+
const filePath = '/app/entry.client.tsx';
|
|
210
|
+
// Mock user confirming the reveal operation
|
|
211
|
+
clackMocks.confirm.mockResolvedValueOnce(true);
|
|
212
|
+
// Mock execSync succeeding
|
|
213
|
+
childProcess.execSync.mockReturnValueOnce('Successfully generated entry files');
|
|
214
|
+
// Mock file existing after reveal
|
|
215
|
+
existsSyncMock.mockReturnValueOnce(true);
|
|
216
|
+
const result = await (0, sdk_setup_1.tryRevealAndGetManualInstructions)(missingFilename, filePath);
|
|
217
|
+
(0, vitest_1.expect)(result).toBe(true);
|
|
218
|
+
(0, vitest_1.expect)(clackMocks.confirm).toHaveBeenCalledWith({
|
|
219
|
+
message: vitest_1.expect.stringContaining('Would you like to try running'),
|
|
220
|
+
initialValue: true,
|
|
221
|
+
});
|
|
222
|
+
(0, vitest_1.expect)(clackMocks.info).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Running'));
|
|
223
|
+
(0, vitest_1.expect)(childProcess.execSync).toHaveBeenCalledWith('npx react-router reveal', {
|
|
224
|
+
encoding: 'utf8',
|
|
225
|
+
stdio: 'pipe',
|
|
226
|
+
});
|
|
227
|
+
(0, vitest_1.expect)(clackMocks.success).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Found entry.client.tsx after running reveal'));
|
|
228
|
+
});
|
|
229
|
+
(0, vitest_1.it)('should return false when user declines reveal operation', async () => {
|
|
230
|
+
const missingFilename = 'entry.server.tsx';
|
|
231
|
+
const filePath = '/app/entry.server.tsx';
|
|
232
|
+
// Mock user declining the reveal operation
|
|
233
|
+
clackMocks.confirm.mockResolvedValueOnce(false);
|
|
234
|
+
const result = await (0, sdk_setup_1.tryRevealAndGetManualInstructions)(missingFilename, filePath);
|
|
235
|
+
(0, vitest_1.expect)(result).toBe(false);
|
|
236
|
+
(0, vitest_1.expect)(clackMocks.confirm).toHaveBeenCalled();
|
|
237
|
+
(0, vitest_1.expect)(childProcess.execSync).not.toHaveBeenCalled();
|
|
238
|
+
(0, vitest_1.expect)(clackMocks.info).not.toHaveBeenCalled();
|
|
239
|
+
});
|
|
240
|
+
(0, vitest_1.it)('should return false when reveal command succeeds but file still does not exist', async () => {
|
|
241
|
+
const missingFilename = 'entry.client.jsx';
|
|
242
|
+
const filePath = '/app/entry.client.jsx';
|
|
243
|
+
// Mock user confirming the reveal operation
|
|
244
|
+
clackMocks.confirm.mockResolvedValueOnce(true);
|
|
245
|
+
// Mock execSync succeeding
|
|
246
|
+
childProcess.execSync.mockReturnValueOnce('Command output');
|
|
247
|
+
// Mock file NOT existing after reveal
|
|
248
|
+
existsSyncMock.mockReturnValueOnce(false);
|
|
249
|
+
const result = await (0, sdk_setup_1.tryRevealAndGetManualInstructions)(missingFilename, filePath);
|
|
250
|
+
(0, vitest_1.expect)(result).toBe(false);
|
|
251
|
+
(0, vitest_1.expect)(childProcess.execSync).toHaveBeenCalled();
|
|
252
|
+
(0, vitest_1.expect)(clackMocks.warn).toHaveBeenCalledWith(vitest_1.expect.stringContaining('entry.client.jsx still not found after running reveal'));
|
|
253
|
+
});
|
|
254
|
+
(0, vitest_1.it)('should return false when reveal command throws an error', async () => {
|
|
255
|
+
const missingFilename = 'entry.server.jsx';
|
|
256
|
+
const filePath = '/app/entry.server.jsx';
|
|
257
|
+
// Mock user confirming the reveal operation
|
|
258
|
+
clackMocks.confirm.mockResolvedValueOnce(true);
|
|
259
|
+
// Mock execSync throwing an error
|
|
260
|
+
const mockError = new Error('Command failed');
|
|
261
|
+
childProcess.execSync.mockImplementationOnce(() => {
|
|
262
|
+
throw mockError;
|
|
263
|
+
});
|
|
264
|
+
const result = await (0, sdk_setup_1.tryRevealAndGetManualInstructions)(missingFilename, filePath);
|
|
265
|
+
(0, vitest_1.expect)(result).toBe(false);
|
|
266
|
+
(0, vitest_1.expect)(childProcess.execSync).toHaveBeenCalled();
|
|
267
|
+
(0, vitest_1.expect)(clackMocks.warn).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Failed to run npx react-router reveal'));
|
|
268
|
+
});
|
|
269
|
+
(0, vitest_1.it)('should log command output when reveal succeeds', async () => {
|
|
270
|
+
const missingFilename = 'entry.client.tsx';
|
|
271
|
+
const filePath = '/app/entry.client.tsx';
|
|
272
|
+
const commandOutput = 'Generated entry files successfully';
|
|
273
|
+
// Mock user confirming the reveal operation
|
|
274
|
+
clackMocks.confirm.mockResolvedValueOnce(true);
|
|
275
|
+
// Mock execSync succeeding with output
|
|
276
|
+
childProcess.execSync.mockReturnValueOnce(commandOutput);
|
|
277
|
+
// Mock file existing after reveal
|
|
278
|
+
existsSyncMock.mockReturnValueOnce(true);
|
|
279
|
+
await (0, sdk_setup_1.tryRevealAndGetManualInstructions)(missingFilename, filePath);
|
|
280
|
+
(0, vitest_1.expect)(clackMocks.info).toHaveBeenCalledWith(commandOutput);
|
|
281
|
+
});
|
|
282
|
+
(0, vitest_1.it)('should handle reveal command with proper parameters', async () => {
|
|
283
|
+
const missingFilename = 'entry.client.tsx';
|
|
284
|
+
const filePath = '/app/entry.client.tsx';
|
|
285
|
+
// Mock user confirming
|
|
286
|
+
clackMocks.confirm.mockResolvedValueOnce(true);
|
|
287
|
+
// Mock execSync succeeding
|
|
288
|
+
childProcess.execSync.mockReturnValueOnce('ok');
|
|
289
|
+
// Mock file existing
|
|
290
|
+
existsSyncMock.mockReturnValueOnce(true);
|
|
291
|
+
await (0, sdk_setup_1.tryRevealAndGetManualInstructions)(missingFilename, filePath);
|
|
292
|
+
(0, vitest_1.expect)(childProcess.execSync).toHaveBeenCalledWith('npx react-router reveal', {
|
|
293
|
+
encoding: 'utf8',
|
|
294
|
+
stdio: 'pipe',
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
(0, vitest_1.describe)('updatePackageJsonScripts', () => {
|
|
299
|
+
(0, vitest_1.beforeEach)(() => {
|
|
300
|
+
vitest_1.vi.clearAllMocks();
|
|
301
|
+
vitest_1.vi.resetAllMocks();
|
|
302
|
+
});
|
|
303
|
+
(0, vitest_1.it)('should set NODE_ENV=production for both dev and start scripts (workaround for React Router v7 + React 19 issue)', async () => {
|
|
304
|
+
const mockPackageJson = {
|
|
305
|
+
scripts: {
|
|
306
|
+
dev: 'react-router dev',
|
|
307
|
+
start: 'react-router serve',
|
|
308
|
+
build: 'react-router build',
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
// Mock getPackageDotJson to return our test package.json
|
|
312
|
+
getPackageDotJsonMock.mockResolvedValue(mockPackageJson);
|
|
313
|
+
// Mock fs.promises.writeFile
|
|
314
|
+
const fsPromises = await import('fs');
|
|
315
|
+
const writeFileMock = vitest_1.vi
|
|
316
|
+
.spyOn(fsPromises.promises, 'writeFile')
|
|
317
|
+
.mockResolvedValue();
|
|
318
|
+
await (0, sdk_setup_1.updatePackageJsonScripts)();
|
|
319
|
+
// Verify writeFile was called
|
|
320
|
+
(0, vitest_1.expect)(writeFileMock).toHaveBeenCalled();
|
|
321
|
+
// Check the written package.json content
|
|
322
|
+
const writtenContent = JSON.parse(writeFileMock.mock.calls[0]?.[1]);
|
|
323
|
+
// Both dev and start scripts should use the correct filenames and commands according to documentation
|
|
324
|
+
(0, vitest_1.expect)(writtenContent.scripts.dev).toBe("NODE_OPTIONS='--import ./instrument.server.mjs' react-router dev");
|
|
325
|
+
// The start script should use react-router-serve with build path according to documentation
|
|
326
|
+
(0, vitest_1.expect)(writtenContent.scripts.start).toBe("NODE_OPTIONS='--import ./instrument.server.mjs' react-router-serve ./build/server/index.js");
|
|
327
|
+
// The build script should remain unchanged
|
|
328
|
+
(0, vitest_1.expect)(writtenContent.scripts.build).toBe('react-router build');
|
|
329
|
+
});
|
|
330
|
+
(0, vitest_1.it)('should handle package.json with only start script', async () => {
|
|
331
|
+
const mockPackageJson = {
|
|
332
|
+
scripts: {
|
|
333
|
+
start: 'react-router serve',
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
// Mock getPackageDotJson to return our test package.json
|
|
337
|
+
getPackageDotJsonMock.mockResolvedValue(mockPackageJson);
|
|
338
|
+
// Mock fs.promises.writeFile
|
|
339
|
+
const fsPromises = await import('fs');
|
|
340
|
+
const writeFileMock = vitest_1.vi
|
|
341
|
+
.spyOn(fsPromises.promises, 'writeFile')
|
|
342
|
+
.mockResolvedValue();
|
|
343
|
+
await (0, sdk_setup_1.updatePackageJsonScripts)();
|
|
344
|
+
// Verify only start script is modified when dev doesn't exist
|
|
345
|
+
const writtenContent = JSON.parse(writeFileMock.mock.calls[0]?.[1]);
|
|
346
|
+
(0, vitest_1.expect)(writtenContent.scripts.start).toBe("NODE_OPTIONS='--import ./instrument.server.mjs' react-router-serve ./build/server/index.js");
|
|
347
|
+
(0, vitest_1.expect)(writtenContent.scripts.dev).toBeUndefined();
|
|
348
|
+
});
|
|
349
|
+
(0, vitest_1.it)('should throw error when no start script exists', async () => {
|
|
350
|
+
const mockPackageJson = {
|
|
351
|
+
scripts: {
|
|
352
|
+
build: 'react-router build',
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
// Mock getPackageDotJson to return package.json without start script
|
|
356
|
+
getPackageDotJsonMock.mockResolvedValue(mockPackageJson);
|
|
357
|
+
await (0, vitest_1.expect)((0, sdk_setup_1.updatePackageJsonScripts)()).rejects.toThrow('Could not find a `start` script in your package.json. Please add: "start": "react-router-serve ./build/server/index.js" and re-run the wizard.');
|
|
358
|
+
});
|
|
359
|
+
(0, vitest_1.it)('should handle unquoted NODE_OPTIONS in dev script', async () => {
|
|
360
|
+
const mockPackageJson = {
|
|
361
|
+
scripts: {
|
|
362
|
+
dev: 'NODE_OPTIONS=--loader ts-node/register react-router dev',
|
|
363
|
+
start: 'react-router serve',
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
getPackageDotJsonMock.mockResolvedValue(mockPackageJson);
|
|
367
|
+
const fsPromises = await import('fs');
|
|
368
|
+
const writeFileMock = vitest_1.vi
|
|
369
|
+
.spyOn(fsPromises.promises, 'writeFile')
|
|
370
|
+
.mockResolvedValue();
|
|
371
|
+
await (0, sdk_setup_1.updatePackageJsonScripts)();
|
|
372
|
+
const writtenContent = JSON.parse(writeFileMock.mock.calls[0]?.[1]);
|
|
373
|
+
// Should merge unquoted NODE_OPTIONS and wrap result in single quotes
|
|
374
|
+
(0, vitest_1.expect)(writtenContent.scripts.dev).toBe("NODE_OPTIONS='--loader ts-node/register --import ./instrument.server.mjs' react-router dev");
|
|
375
|
+
});
|
|
376
|
+
(0, vitest_1.it)('should handle unquoted NODE_OPTIONS in start script', async () => {
|
|
377
|
+
const mockPackageJson = {
|
|
378
|
+
scripts: {
|
|
379
|
+
start: 'NODE_OPTIONS=--require ./dotenv-config.js react-router-serve ./build/server/index.js',
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
getPackageDotJsonMock.mockResolvedValue(mockPackageJson);
|
|
383
|
+
const fsPromises = await import('fs');
|
|
384
|
+
const writeFileMock = vitest_1.vi
|
|
385
|
+
.spyOn(fsPromises.promises, 'writeFile')
|
|
386
|
+
.mockResolvedValue();
|
|
387
|
+
await (0, sdk_setup_1.updatePackageJsonScripts)();
|
|
388
|
+
const writtenContent = JSON.parse(writeFileMock.mock.calls[0]?.[1]);
|
|
389
|
+
// Should merge unquoted NODE_OPTIONS and wrap result in single quotes
|
|
390
|
+
(0, vitest_1.expect)(writtenContent.scripts.start).toBe("NODE_OPTIONS='--require ./dotenv-config.js --import ./instrument.server.mjs' react-router-serve ./build/server/index.js");
|
|
391
|
+
});
|
|
392
|
+
(0, vitest_1.it)('should handle quoted NODE_OPTIONS and standardize to single quotes', async () => {
|
|
393
|
+
const mockPackageJson = {
|
|
394
|
+
scripts: {
|
|
395
|
+
dev: 'NODE_OPTIONS="--max-old-space-size=4096" react-router dev',
|
|
396
|
+
start: "NODE_OPTIONS='--enable-source-maps' react-router serve",
|
|
397
|
+
},
|
|
398
|
+
};
|
|
399
|
+
getPackageDotJsonMock.mockResolvedValue(mockPackageJson);
|
|
400
|
+
const fsPromises = await import('fs');
|
|
401
|
+
const writeFileMock = vitest_1.vi
|
|
402
|
+
.spyOn(fsPromises.promises, 'writeFile')
|
|
403
|
+
.mockResolvedValue();
|
|
404
|
+
await (0, sdk_setup_1.updatePackageJsonScripts)();
|
|
405
|
+
const writtenContent = JSON.parse(writeFileMock.mock.calls[0]?.[1]);
|
|
406
|
+
// Should standardize to single quotes
|
|
407
|
+
(0, vitest_1.expect)(writtenContent.scripts.dev).toBe("NODE_OPTIONS='--max-old-space-size=4096 --import ./instrument.server.mjs' react-router dev");
|
|
408
|
+
(0, vitest_1.expect)(writtenContent.scripts.start).toBe("NODE_OPTIONS='--enable-source-maps --import ./instrument.server.mjs' react-router serve");
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
//# sourceMappingURL=sdk-setup.test.js.map
|