|
| 1 | +import fs from 'node:fs'; |
| 2 | +import os from 'node:os'; |
| 3 | +import path from 'node:path'; |
| 4 | +import { exportTemplates, getUnsafeOutDirMessage } from '../export.js'; |
| 5 | + |
| 6 | +const homeDirectory = path.resolve('/home/user'); |
| 7 | +const cwd = path.join(homeDirectory, 'project'); |
| 8 | +const emailsDirectoryPath = './emails'; |
| 9 | + |
| 10 | +const getMessage = (outDir: string, emailsDirectory = emailsDirectoryPath) => |
| 11 | + getUnsafeOutDirMessage(outDir, emailsDirectory, { cwd, homeDirectory }); |
| 12 | + |
| 13 | +test('blocks the filesystem root', () => { |
| 14 | + expect(getMessage(path.parse(cwd).root)).toBe( |
| 15 | + "--outDir can't be the filesystem root.", |
| 16 | + ); |
| 17 | +}); |
| 18 | + |
| 19 | +test('blocks the home directory', () => { |
| 20 | + expect(getMessage(homeDirectory)).toBe( |
| 21 | + "--outDir can't be your home directory.", |
| 22 | + ); |
| 23 | + expect(getMessage('..')).toBe("--outDir can't be your home directory."); |
| 24 | +}); |
| 25 | + |
| 26 | +test('blocks a parent of the home directory', () => { |
| 27 | + expect(getMessage(path.dirname(homeDirectory))).toBe( |
| 28 | + "--outDir can't contain your home directory.", |
| 29 | + ); |
| 30 | +}); |
| 31 | + |
| 32 | +test('blocks the project directory', () => { |
| 33 | + expect(getMessage('.')).toBe("--outDir can't be the project directory."); |
| 34 | + expect(getMessage(cwd)).toBe("--outDir can't be the project directory."); |
| 35 | +}); |
| 36 | + |
| 37 | +test('blocks a parent of the project directory', () => { |
| 38 | + const nestedCwd = path.join(cwd, 'apps', 'emails'); |
| 39 | + expect( |
| 40 | + getUnsafeOutDirMessage('..', emailsDirectoryPath, { |
| 41 | + cwd: nestedCwd, |
| 42 | + homeDirectory, |
| 43 | + }), |
| 44 | + ).toBe("--outDir can't contain the project directory."); |
| 45 | +}); |
| 46 | + |
| 47 | +test('blocks the email templates directory and its parents', () => { |
| 48 | + const message = "--outDir can't contain your email templates."; |
| 49 | + expect(getMessage('emails')).toBe(message); |
| 50 | + expect(getMessage('./emails/')).toBe(message); |
| 51 | + expect(getMessage(path.join(cwd, 'emails'))).toBe(message); |
| 52 | + expect(getMessage('src', './src/emails')).toBe(message); |
| 53 | +}); |
| 54 | + |
| 55 | +test('blocks the static assets directory', () => { |
| 56 | + const message = "--outDir can't be your static assets directory."; |
| 57 | + expect(getMessage('emails/static')).toBe(message); |
| 58 | + expect(getMessage('emails/static/images')).toBe(message); |
| 59 | +}); |
| 60 | + |
| 61 | +test('allows other directories', () => { |
| 62 | + expect(getMessage('out')).toBeUndefined(); |
| 63 | + expect(getMessage('out/nested')).toBeUndefined(); |
| 64 | + expect(getMessage('emails/out')).toBeUndefined(); |
| 65 | + expect(getMessage('../sibling')).toBeUndefined(); |
| 66 | + expect(getMessage('/tmp/react-email-out')).toBeUndefined(); |
| 67 | +}); |
| 68 | + |
| 69 | +test('refuses to remove the project directory and keeps files intact', async () => { |
| 70 | + const projectDir = fs.mkdtempSync(path.join(os.tmpdir(), 'export-guard-')); |
| 71 | + const emailsDir = path.join(projectDir, 'emails'); |
| 72 | + fs.mkdirSync(emailsDir, { recursive: true }); |
| 73 | + fs.writeFileSync( |
| 74 | + path.join(emailsDir, 'welcome.tsx'), |
| 75 | + 'export default () => null;', |
| 76 | + ); |
| 77 | + fs.writeFileSync(path.join(projectDir, 'package.json'), '{}'); |
| 78 | + |
| 79 | + const cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue(projectDir); |
| 80 | + const exitSpy = vi.spyOn(process, 'exit').mockImplementation((( |
| 81 | + code?: number, |
| 82 | + ) => { |
| 83 | + throw new Error(`process.exit(${code})`); |
| 84 | + }) as never); |
| 85 | + const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); |
| 86 | + |
| 87 | + try { |
| 88 | + await expect( |
| 89 | + exportTemplates('.', 'emails', { silent: true }), |
| 90 | + ).rejects.toThrow('process.exit(1)'); |
| 91 | + |
| 92 | + expect(errorSpy).toHaveBeenCalledWith( |
| 93 | + `--outDir can't be the project directory. Choose a different --outDir, such as "out".`, |
| 94 | + ); |
| 95 | + expect(fs.existsSync(path.join(projectDir, 'package.json'))).toBe(true); |
| 96 | + expect(fs.existsSync(path.join(emailsDir, 'welcome.tsx'))).toBe(true); |
| 97 | + } finally { |
| 98 | + cwdSpy.mockRestore(); |
| 99 | + exitSpy.mockRestore(); |
| 100 | + errorSpy.mockRestore(); |
| 101 | + fs.rmSync(projectDir, { recursive: true, force: true }); |
| 102 | + } |
| 103 | +}); |
0 commit comments