@reapit/elements 5.0.0-beta.71 → 5.0.0-beta.73

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.
@@ -1,178 +0,0 @@
1
- import { writeFileSync } from 'node:fs'
2
- import { join, dirname } from 'node:path'
3
- import { fileURLToPath } from 'node:url'
4
- import { listCodemods, getCodemodReadme, getCodemodDescription, validateCodemodName } from '../codemods'
5
-
6
- const __filename = fileURLToPath(import.meta.url)
7
- const __dirname = dirname(__filename)
8
- const codemodDir = join(__dirname, '..')
9
-
10
- describe('listCodemods', () => {
11
- test('returns codemods from manifest', () => {
12
- const result = listCodemods()
13
-
14
- // Should return at least the codemods we know exist
15
- expect(result).toContain('at-a-glance-article-card')
16
- expect(Array.isArray(result)).toBe(true)
17
- })
18
-
19
- test('returns a copy to prevent external mutation', () => {
20
- const result1 = listCodemods()
21
- const result2 = listCodemods()
22
-
23
- expect(result1).toEqual(result2)
24
- expect(result1).not.toBe(result2) // Different array instances
25
- })
26
- })
27
-
28
- describe('getCodemodReadme', () => {
29
- test('returns README content when it exists', () => {
30
- const result = getCodemodReadme('at-a-glance-article-card')
31
-
32
- expect(result).not.toBeNull()
33
- expect(typeof result).toBe('string')
34
- expect(result!.length).toBeGreaterThan(0)
35
- })
36
-
37
- test('returns null when README does not exist', () => {
38
- const result = getCodemodReadme('nonexistent-codemod' as any)
39
-
40
- expect(result).toBeNull()
41
- })
42
-
43
- test('reads complete README with front matter and body', () => {
44
- const result = getCodemodReadme('at-a-glance-article-card')
45
-
46
- expect(result).not.toBeNull()
47
- // Check for front matter markers
48
- expect(result).toContain('---')
49
- // Check for typical README content
50
- expect(result).toMatch(/[#\s]/i)
51
- })
52
- })
53
-
54
- describe('getCodemodDescription', () => {
55
- test('returns description from manifest', () => {
56
- const result = getCodemodDescription('at-a-glance-article-card')
57
-
58
- expect(result).toBe('Migrates AtAGlance.Card to AtAGlance.ArticleCard')
59
- })
60
-
61
- test('returns null when codemod does not exist in manifest', () => {
62
- const result = getCodemodDescription('nonexistent-codemod')
63
-
64
- expect(result).toBeNull()
65
- })
66
-
67
- test('returns description for all codemods in manifest', () => {
68
- const codemods = listCodemods()
69
-
70
- for (const name of codemods) {
71
- const description = getCodemodDescription(name)
72
- // Description can be null or a string
73
- expect(description === null || typeof description === 'string').toBe(true)
74
- }
75
- })
76
- })
77
-
78
- describe('validateCodemodName', () => {
79
- test('returns name when codemod exists in manifest', () => {
80
- const result = validateCodemodName('at-a-glance-article-card')
81
-
82
- expect(result).toBe('at-a-glance-article-card')
83
- })
84
-
85
- test('returns null when codemod does not exist', () => {
86
- const result = validateCodemodName('nonexistent-codemod')
87
-
88
- expect(result).toBeNull()
89
- })
90
-
91
- test('prevents path traversal attacks with ../', () => {
92
- const result = validateCodemodName('../../../etc/passwd')
93
-
94
- expect(result).toBeNull()
95
- })
96
-
97
- test('prevents path traversal attacks with absolute paths', () => {
98
- const result = validateCodemodName('/etc/passwd')
99
-
100
- expect(result).toBeNull()
101
- })
102
-
103
- test('prevents path traversal with encoded characters', () => {
104
- const result = validateCodemodName('..%2F..%2Fetc%2Fpasswd')
105
-
106
- expect(result).toBeNull()
107
- })
108
-
109
- test('prevents path components in name', () => {
110
- const result = validateCodemodName('at-a-glance-article-card/index.ts')
111
-
112
- expect(result).toBeNull()
113
- })
114
-
115
- test('case-sensitive validation', () => {
116
- const result = validateCodemodName('AT-A-GLANCE-ARTICLE-CARD')
117
-
118
- expect(result).toBeNull()
119
- })
120
-
121
- test('validates all codemods from manifest correctly', () => {
122
- const codemods = listCodemods()
123
-
124
- for (const name of codemods) {
125
- const validated = validateCodemodName(name)
126
- expect(validated).toBe(name)
127
- }
128
- })
129
- })
130
-
131
- describe('integration tests', () => {
132
- test('workflow: list codemods, validate, and get description', () => {
133
- // List codemods
134
- const codemods = listCodemods()
135
- expect(codemods.length).toBeGreaterThan(0)
136
- expect(codemods).toContain('at-a-glance-article-card')
137
-
138
- // Validate a known codemod
139
- const validated = validateCodemodName('at-a-glance-article-card')
140
- expect(validated).toBe('at-a-glance-article-card')
141
-
142
- // Get description
143
- const description = getCodemodDescription(validated!)
144
- expect(description).toBe('Migrates AtAGlance.Card to AtAGlance.ArticleCard')
145
- })
146
-
147
- test('workflow: attempt to access invalid codemod', () => {
148
- // List shows only valid codemods
149
- const codemods = listCodemods()
150
- expect(codemods.length).toBeGreaterThan(0)
151
-
152
- // Attempt to validate malicious input
153
- const validated = validateCodemodName('../../etc/passwd')
154
- expect(validated).toBeNull()
155
-
156
- // No description accessible for invalid name
157
- const description = getCodemodDescription('../../etc/passwd')
158
- expect(description).toBeNull()
159
- })
160
-
161
- test('consistency: manifest and file system match', () => {
162
- const codemods = listCodemods()
163
-
164
- // All codemods in manifest should have their directories accessible
165
- for (const name of codemods) {
166
- const validated = validateCodemodName(name)
167
- expect(validated).toBe(name)
168
-
169
- // Should be able to get README (or null if missing, which is OK)
170
- const readme = getCodemodReadme(name)
171
- expect(readme === null || typeof readme === 'string').toBe(true)
172
-
173
- // Should be able to get description from manifest
174
- const description = getCodemodDescription(name)
175
- expect(description === null || typeof description === 'string').toBe(true)
176
- }
177
- })
178
- })
@@ -1,240 +0,0 @@
1
- import { existsSync, mkdirSync, writeFileSync, rmSync } from 'node:fs'
2
- import { join } from 'node:path'
3
- import { tmpdir } from 'node:os'
4
- import { discoverCodemods, getCodemodMetadata } from '../generate-manifest'
5
-
6
- let testDir: string
7
-
8
- beforeEach(() => {
9
- // Create a temporary test directory
10
- testDir = join(tmpdir(), `codemods-test-${Date.now()}`)
11
- mkdirSync(testDir, { recursive: true })
12
- })
13
-
14
- afterEach(() => {
15
- // Clean up test directory
16
- if (existsSync(testDir)) {
17
- rmSync(testDir, { recursive: true, force: true })
18
- }
19
- })
20
-
21
- describe('discoverCodemods', () => {
22
- test('returns empty array when directory has no codemods', () => {
23
- const result = discoverCodemods(testDir)
24
- expect(result).toEqual([])
25
- })
26
-
27
- test('returns codemod names that have transform.ts files', () => {
28
- // Create valid codemod directories
29
- const codemod1Dir = join(testDir, 'codemod-1')
30
- const codemod2Dir = join(testDir, 'codemod-2')
31
-
32
- mkdirSync(codemod1Dir)
33
- mkdirSync(codemod2Dir)
34
-
35
- writeFileSync(join(codemod1Dir, 'transform.ts'), 'export default function() {}')
36
- writeFileSync(join(codemod2Dir, 'transform.ts'), 'export default function() {}')
37
-
38
- const result = discoverCodemods(testDir)
39
-
40
- expect(result).toHaveLength(2)
41
- expect(result).toContain('codemod-1')
42
- expect(result).toContain('codemod-2')
43
- })
44
-
45
- test('ignores directories without transform.ts files', () => {
46
- const validDir = join(testDir, 'valid-codemod')
47
- const invalidDir = join(testDir, 'invalid-codemod')
48
-
49
- mkdirSync(validDir)
50
- mkdirSync(invalidDir)
51
-
52
- writeFileSync(join(validDir, 'transform.ts'), 'export default function() {}')
53
- writeFileSync(join(invalidDir, 'index.ts'), 'export default function() {}')
54
-
55
- const result = discoverCodemods(testDir)
56
-
57
- expect(result).toHaveLength(1)
58
- expect(result).toContain('valid-codemod')
59
- expect(result).not.toContain('invalid-codemod')
60
- })
61
-
62
- test('ignores files in the root directory', () => {
63
- const validDir = join(testDir, 'valid-codemod')
64
- mkdirSync(validDir)
65
- writeFileSync(join(validDir, 'transform.ts'), 'export default function() {}')
66
-
67
- // Create some files in root that should be ignored
68
- writeFileSync(join(testDir, 'transform.ts'), 'export default function() {}')
69
- writeFileSync(join(testDir, 'runner.ts'), 'export function run() {}')
70
-
71
- const result = discoverCodemods(testDir)
72
-
73
- expect(result).toHaveLength(1)
74
- expect(result).toContain('valid-codemod')
75
- })
76
-
77
- test('returns codemods in consistent alphabetical order', () => {
78
- const names = ['zebra', 'alpha', 'beta']
79
-
80
- for (const name of names) {
81
- const dir = join(testDir, name)
82
- mkdirSync(dir)
83
- writeFileSync(join(dir, 'transform.ts'), 'export default function() {}')
84
- }
85
-
86
- const result = discoverCodemods(testDir)
87
-
88
- expect(result).toEqual(['alpha', 'beta', 'zebra'])
89
- })
90
-
91
- test('ignores special directories', () => {
92
- const validDir = join(testDir, 'valid-codemod')
93
- const nodeModulesDir = join(testDir, 'node_modules')
94
- const testsDir = join(testDir, '__tests__')
95
- const hiddenDir = join(testDir, '.hidden')
96
-
97
- mkdirSync(validDir)
98
- mkdirSync(nodeModulesDir)
99
- mkdirSync(testsDir)
100
- mkdirSync(hiddenDir)
101
-
102
- writeFileSync(join(validDir, 'transform.ts'), 'export default function() {}')
103
- writeFileSync(join(nodeModulesDir, 'transform.ts'), 'export default function() {}')
104
- writeFileSync(join(testsDir, 'transform.ts'), 'export default function() {}')
105
- writeFileSync(join(hiddenDir, 'transform.ts'), 'export default function() {}')
106
-
107
- const result = discoverCodemods(testDir)
108
-
109
- expect(result).toHaveLength(1)
110
- expect(result).toContain('valid-codemod')
111
- })
112
-
113
- test('handles non-existent directory gracefully', () => {
114
- const nonExistentDir = join(testDir, 'does-not-exist')
115
- const result = discoverCodemods(nonExistentDir)
116
-
117
- expect(result).toEqual([])
118
- })
119
- })
120
-
121
- describe('getCodemodMetadata', () => {
122
- test('extracts description from README front matter', () => {
123
- const codemodDir = join(testDir, 'test-codemod')
124
- mkdirSync(codemodDir)
125
-
126
- const readmeContent = `---
127
- description: Transforms old API to new API
128
- ---
129
- # Documentation
130
-
131
- Body content.`
132
-
133
- writeFileSync(join(codemodDir, 'README.md'), readmeContent)
134
-
135
- const result = getCodemodMetadata(testDir, 'test-codemod')
136
-
137
- expect(result).toEqual({
138
- name: 'test-codemod',
139
- description: 'Transforms old API to new API',
140
- })
141
- })
142
-
143
- test('returns null description when README does not exist', () => {
144
- const codemodDir = join(testDir, 'test-codemod')
145
- mkdirSync(codemodDir)
146
-
147
- const result = getCodemodMetadata(testDir, 'test-codemod')
148
-
149
- expect(result).toEqual({
150
- name: 'test-codemod',
151
- description: null,
152
- })
153
- })
154
-
155
- test('returns null description when README has no front matter', () => {
156
- const codemodDir = join(testDir, 'test-codemod')
157
- mkdirSync(codemodDir)
158
-
159
- writeFileSync(join(codemodDir, 'README.md'), '# Test\n\nNo front matter.')
160
-
161
- const result = getCodemodMetadata(testDir, 'test-codemod')
162
-
163
- expect(result).toEqual({
164
- name: 'test-codemod',
165
- description: null,
166
- })
167
- })
168
-
169
- test('returns null description when front matter has no description', () => {
170
- const codemodDir = join(testDir, 'test-codemod')
171
- mkdirSync(codemodDir)
172
-
173
- const readmeContent = `---
174
- author: John Doe
175
- ---
176
- # Documentation`
177
-
178
- writeFileSync(join(codemodDir, 'README.md'), readmeContent)
179
-
180
- const result = getCodemodMetadata(testDir, 'test-codemod')
181
-
182
- expect(result).toEqual({
183
- name: 'test-codemod',
184
- description: null,
185
- })
186
- })
187
-
188
- test('extracts description with special characters', () => {
189
- const codemodDir = join(testDir, 'test-codemod')
190
- mkdirSync(codemodDir)
191
-
192
- const readmeContent = `---
193
- description: Transforms @reapit/elements v4 -> v5
194
- ---
195
- # Documentation`
196
-
197
- writeFileSync(join(codemodDir, 'README.md'), readmeContent)
198
-
199
- const result = getCodemodMetadata(testDir, 'test-codemod')
200
-
201
- expect(result).toEqual({
202
- name: 'test-codemod',
203
- description: 'Transforms @reapit/elements v4 -> v5',
204
- })
205
- })
206
- })
207
-
208
- describe('integration', () => {
209
- test('workflow: discover codemods and extract metadata', () => {
210
- // Create codemods
211
- const codemod1Dir = join(testDir, 'my-codemod')
212
- const codemod2Dir = join(testDir, 'another-codemod')
213
-
214
- mkdirSync(codemod1Dir)
215
- mkdirSync(codemod2Dir)
216
-
217
- writeFileSync(join(codemod1Dir, 'transform.ts'), 'export default function() {}')
218
- writeFileSync(
219
- join(codemod1Dir, 'README.md'),
220
- `---
221
- description: My awesome codemod
222
- ---
223
- # My Codemod`,
224
- )
225
-
226
- writeFileSync(join(codemod2Dir, 'transform.ts'), 'export default function() {}')
227
-
228
- // Discover codemods
229
- const codemods = discoverCodemods(testDir)
230
- expect(codemods).toEqual(['another-codemod', 'my-codemod'])
231
-
232
- // Get metadata for each
233
- const metadata = codemods.map((name) => getCodemodMetadata(testDir, name))
234
-
235
- expect(metadata).toEqual([
236
- { name: 'another-codemod', description: null },
237
- { name: 'my-codemod', description: 'My awesome codemod' },
238
- ])
239
- })
240
- })
@@ -1,218 +0,0 @@
1
- import { parseFrontMatter } from '../readme-parser'
2
-
3
- describe('parseFrontMatter', () => {
4
- test('parses front matter with description', () => {
5
- const content = `---
6
- description: My description
7
- ---
8
- # My Content
9
-
10
- Some body text.`
11
-
12
- const result = parseFrontMatter(content)
13
-
14
- expect(result).toEqual({
15
- description: 'My description',
16
- body: '# My Content\n\nSome body text.',
17
- })
18
- })
19
-
20
- test('parses front matter with multiple fields, extracting only description', () => {
21
- const content = `---
22
- description: Transform components
23
- author: John Doe
24
- version: 1.0.0
25
- ---
26
- # Documentation
27
-
28
- This is the body.`
29
-
30
- const result = parseFrontMatter(content)
31
-
32
- expect(result).toEqual({
33
- description: 'Transform components',
34
- body: '# Documentation\n\nThis is the body.',
35
- })
36
- })
37
-
38
- test('returns body only when no front matter exists', () => {
39
- const content = `# My Content
40
-
41
- This has no front matter.`
42
-
43
- const result = parseFrontMatter(content)
44
-
45
- expect(result).toEqual({
46
- body: content,
47
- })
48
- })
49
-
50
- test('returns body only when front matter has no description', () => {
51
- const content = `---
52
- author: Jane Smith
53
- ---
54
- # Content
55
-
56
- Body text here.`
57
-
58
- const result = parseFrontMatter(content)
59
-
60
- expect(result).toEqual({
61
- description: undefined,
62
- body: '# Content\n\nBody text here.',
63
- })
64
- })
65
-
66
- test('handles empty body after front matter', () => {
67
- const content = `---
68
- description: Empty body
69
- ---
70
- `
71
-
72
- const result = parseFrontMatter(content)
73
-
74
- expect(result).toEqual({
75
- description: 'Empty body',
76
- body: '',
77
- })
78
- })
79
-
80
- test('handles empty content', () => {
81
- const content = ''
82
-
83
- const result = parseFrontMatter(content)
84
-
85
- expect(result).toEqual({
86
- body: '',
87
- })
88
- })
89
-
90
- test('trims whitespace from body', () => {
91
- const content = `---
92
- description: Test
93
- ---
94
-
95
-
96
- # Header
97
-
98
- Content
99
-
100
-
101
- `
102
-
103
- const result = parseFrontMatter(content)
104
-
105
- expect(result.body).toBe('# Header\n\nContent')
106
- })
107
-
108
- test('handles description with special characters', () => {
109
- const content = `---
110
- description: Transforms @reapit/elements v4 -> v5
111
- ---
112
- Body content`
113
-
114
- const result = parseFrontMatter(content)
115
-
116
- expect(result.description).toBe('Transforms @reapit/elements v4 -> v5')
117
- })
118
-
119
- test('handles description with quotes', () => {
120
- const content = `---
121
- description: "Quoted description"
122
- ---
123
- Body`
124
-
125
- const result = parseFrontMatter(content)
126
-
127
- expect(result.description).toBe('"Quoted description"')
128
- })
129
-
130
- test('handles multiline content in body', () => {
131
- const content = `---
132
- description: Short desc
133
- ---
134
- # Heading 1
135
-
136
- Paragraph 1
137
-
138
- ## Heading 2
139
-
140
- Paragraph 2
141
-
142
- \`\`\`typescript
143
- code here
144
- \`\`\`
145
-
146
- Final paragraph.`
147
-
148
- const result = parseFrontMatter(content)
149
-
150
- expect(result.body).toContain('# Heading 1')
151
- expect(result.body).toContain('## Heading 2')
152
- expect(result.body).toContain('```typescript')
153
- expect(result.body).toContain('Final paragraph.')
154
- })
155
-
156
- test('does not parse invalid front matter format', () => {
157
- const content = `--
158
- description: Invalid
159
- --
160
- Body`
161
-
162
- const result = parseFrontMatter(content)
163
-
164
- expect(result).toEqual({
165
- body: content,
166
- })
167
- })
168
-
169
- test('handles front matter without closing delimiter', () => {
170
- const content = `---
171
- description: No closing
172
- Body content`
173
-
174
- const result = parseFrontMatter(content)
175
-
176
- expect(result).toEqual({
177
- body: content,
178
- })
179
- })
180
-
181
- test('extracts first description when multiple exist', () => {
182
- const content = `---
183
- description: First description
184
- description: Second description
185
- ---
186
- Body`
187
-
188
- const result = parseFrontMatter(content)
189
-
190
- expect(result.description).toBe('First description')
191
- })
192
-
193
- test('handles description with colons in value', () => {
194
- const content = `---
195
- description: Transform: Old API to New API
196
- ---
197
- Body`
198
-
199
- const result = parseFrontMatter(content)
200
-
201
- expect(result.description).toBe('Transform: Old API to New API')
202
- })
203
-
204
- test('preserves newlines in body', () => {
205
- const content = `---
206
- description: Test
207
- ---
208
- Line 1
209
-
210
- Line 2
211
-
212
- Line 3`
213
-
214
- const result = parseFrontMatter(content)
215
-
216
- expect(result.body).toBe('Line 1\n\nLine 2\n\nLine 3')
217
- })
218
- })