1import assert from 'assert'; 2import { vol } from 'memfs'; 3import path from 'path'; 4 5import { 6 writeAssetMapAsync, 7 writeBundlesAsync, 8 writeDebugHtmlAsync, 9 writeSourceMapsAsync, 10} from '../writeContents'; 11 12describe(writeDebugHtmlAsync, () => { 13 afterEach(() => { 14 vol.reset(); 15 }); 16 17 it(`creates a debug html file`, async () => { 18 const projectRoot = '/'; 19 await writeDebugHtmlAsync({ 20 outputDir: projectRoot, 21 fileNames: { ios: 'index.ios.js', android: 'index.android.js', windows: 'index.windows.js' }, 22 }); 23 expect(vol.readFileSync(path.join(projectRoot, 'debug.html'), 'utf8')).toMatchSnapshot(); 24 }); 25}); 26 27describe(writeBundlesAsync, () => { 28 afterEach(() => { 29 vol.reset(); 30 }); 31 it(`writes JS bundles to disk`, async () => { 32 const projectRoot = '/'; 33 34 const contents = `var foo = true;`; 35 const results = await writeBundlesAsync({ 36 outputDir: projectRoot, 37 bundles: { 38 ios: { 39 code: contents, 40 }, 41 android: { 42 code: contents, 43 }, 44 }, 45 }); 46 47 expect(results.fileNames.android).toBeDefined(); 48 49 expect(results.fileNames.ios).toBe('ios-4fe3891dcaca43901bd8797db78405e4.js'); 50 expect(results.hashes).toStrictEqual({ 51 ios: expect.any(String), 52 android: expect.any(String), 53 }); 54 expect(vol.readFileSync(path.join(projectRoot, results.fileNames.ios!), 'utf8')).toBe(contents); 55 }); 56 it(`writes hbc bundles to disk`, async () => { 57 const projectRoot = '/'; 58 const contents = Uint8Array.from([1, 2, 3]); 59 const results = await writeBundlesAsync({ 60 outputDir: projectRoot, 61 bundles: { 62 ios: { 63 // this overwrites js code if present 64 hermesBytecodeBundle: contents, 65 code: 'var foo = true;', 66 }, 67 }, 68 }); 69 70 expect(results.fileNames.ios).toBe('ios-5289df737df57326fcdd22597afb1fac.hbc'); 71 expect(results.hashes).toStrictEqual({ 72 ios: expect.any(String), 73 }); 74 expect(vol.readFileSync(path.join(projectRoot, results.fileNames.ios!))).toBeDefined(); 75 }); 76}); 77 78describe(writeAssetMapAsync, () => { 79 afterEach(() => { 80 vol.reset(); 81 }); 82 it(`writes asset map to disk`, async () => { 83 const projectRoot = '/'; 84 85 const results = await writeAssetMapAsync({ 86 outputDir: projectRoot, 87 assets: [{ hash: 'alpha' }, { hash: 'beta' }] as any, 88 }); 89 90 expect(results).toStrictEqual({ 91 alpha: { hash: 'alpha' }, 92 beta: { hash: 'beta' }, 93 }); 94 95 expect( 96 JSON.parse(vol.readFileSync(path.join(projectRoot, 'assetmap.json'), 'utf8') as string) 97 ).toBeDefined(); 98 }); 99}); 100 101describe(writeSourceMapsAsync, () => { 102 afterEach(() => { 103 vol.reset(); 104 }); 105 106 it(`writes source maps to disk`, async () => { 107 const projectRoot = '/'; 108 109 // User wrote this 110 const contents = `var foo = true;\ninvalid-source-map-comment`; 111 112 // Metro made this 113 const bundles = { 114 ios: { 115 code: contents, 116 map: 'ios_map', 117 }, 118 android: { 119 code: contents, 120 map: 'android_map', 121 }, 122 }; 123 124 // Expo persists the code and returns info 125 const jsResults = await writeBundlesAsync({ 126 outputDir: projectRoot, 127 bundles, 128 }); 129 130 // Expo also modifies the source maps and persists 131 const results = await writeSourceMapsAsync({ 132 outputDir: projectRoot, 133 hashes: jsResults.hashes, 134 fileNames: jsResults.fileNames, 135 bundles, 136 }); 137 138 for (const item of results) { 139 assert(item); 140 expect(vol.readFileSync(path.join(projectRoot, item.fileName), 'utf8')).toBe(item.map); 141 expect( 142 vol.readFileSync(path.join(projectRoot, `${item.platform}-${item.hash}.js`), 'utf8') 143 ).toMatch(/\/\/# sourceMappingURL=/); 144 // // Removed by `removeOriginalSourceMappingUrl` 145 // expect( 146 // vol.readFileSync(path.join(projectRoot, `${item.platform}-${item.hash}.js`), 'utf8') 147 // ).not.toMatch(/invalid-source-map-comment/); 148 } 149 }); 150 151 it(`skips writing when the map is not defined`, async () => { 152 const projectRoot = '/'; 153 154 // User wrote this 155 const contents = `var foo = true;\ninvalid-source-map-comment`; 156 157 // Metro made this 158 const bundles = { 159 ios: { 160 code: contents, 161 }, 162 android: { 163 code: contents, 164 map: 'android_map', 165 }, 166 }; 167 168 // Expo persists the code and returns info 169 const jsResults = await writeBundlesAsync({ 170 outputDir: projectRoot, 171 bundles, 172 }); 173 174 // Expo also modifies the source maps and persists 175 const results = await writeSourceMapsAsync({ 176 outputDir: projectRoot, 177 hashes: jsResults.hashes, 178 fileNames: jsResults.fileNames, 179 bundles, 180 }); 181 182 expect(results).toHaveLength(1); 183 }); 184}); 185