|
| 1 | +import {readFile, readdir, stat} from 'node:fs/promises' |
| 2 | +import path from 'node:path' |
| 3 | +import {describe, expect, it} from '@rstest/core' |
| 4 | +import { |
| 5 | + createRealProject, |
| 6 | + pathExists, |
| 7 | + runCli, |
| 8 | + writeProjectFile, |
| 9 | +} from '../support/real-project' |
| 10 | + |
| 11 | +async function listFiles(root: string, relativeDir = ''): Promise<string[]> { |
| 12 | + const dir = path.join(root, relativeDir) |
| 13 | + const entries = await readdir(dir, {withFileTypes: true}) |
| 14 | + const files: string[] = [] |
| 15 | + for (const entry of entries) { |
| 16 | + const nextRelative = path.join(relativeDir, entry.name) |
| 17 | + const nextPath = path.join(root, nextRelative) |
| 18 | + if (entry.isDirectory()) { |
| 19 | + files.push(...(await listFiles(root, nextRelative))) |
| 20 | + } else if ((await stat(nextPath)).isFile()) { |
| 21 | + files.push(nextRelative) |
| 22 | + } |
| 23 | + } |
| 24 | + return files.sort() |
| 25 | +} |
| 26 | + |
| 27 | +async function findFirst(root: string, predicate: (file: string) => boolean) { |
| 28 | + const files = await listFiles(root) |
| 29 | + return files.find(predicate) |
| 30 | +} |
| 31 | + |
| 32 | +describe('cli config build', () => { |
| 33 | + it('builds a real fixture with env vars, aliases, assets, and sourcemaps', async () => { |
| 34 | + const project = await createRealProject('cli-config-build') |
| 35 | + try { |
| 36 | + await writeProjectFile( |
| 37 | + project.root, |
| 38 | + 'package.json', |
| 39 | + JSON.stringify( |
| 40 | + { |
| 41 | + name: 'cli-config-build-fixture', |
| 42 | + private: true, |
| 43 | + type: 'module', |
| 44 | + }, |
| 45 | + null, |
| 46 | + 2, |
| 47 | + ) + '\n', |
| 48 | + ) |
| 49 | + |
| 50 | + await writeProjectFile( |
| 51 | + project.root, |
| 52 | + 'tsconfig.json', |
| 53 | + JSON.stringify( |
| 54 | + { |
| 55 | + compilerOptions: { |
| 56 | + target: 'ES2020', |
| 57 | + module: 'ESNext', |
| 58 | + moduleResolution: 'Bundler', |
| 59 | + lib: ['ES2020', 'DOM'], |
| 60 | + baseUrl: '.', |
| 61 | + strict: true, |
| 62 | + skipLibCheck: true, |
| 63 | + }, |
| 64 | + }, |
| 65 | + null, |
| 66 | + 2, |
| 67 | + ) + '\n', |
| 68 | + ) |
| 69 | + |
| 70 | + await writeProjectFile( |
| 71 | + project.root, |
| 72 | + 'emp.config.ts', |
| 73 | + [ |
| 74 | + 'export default store => ({', |
| 75 | + " base: '/cdn/',", |
| 76 | + " appSrc: 'src',", |
| 77 | + " appEntry: 'main.ts',", |
| 78 | + " build: {", |
| 79 | + " outDir: 'dist-real',", |
| 80 | + " assetsDir: 'assets-real',", |
| 81 | + " sourcemap: {js: 'source-map', css: true},", |
| 82 | + ' },', |
| 83 | + " html: {mountId: 'custom-root', title: 'CLI Real Config'},", |
| 84 | + " entries: {main: {}},", |
| 85 | + " define: {", |
| 86 | + " __EMP_TEST_ENV__: JSON.stringify('from-define'),", |
| 87 | + " API_URL: JSON.stringify(store.cliOptions.envVars?.API_URL ?? ''),", |
| 88 | + ' },', |
| 89 | + " defineFix: 'all',", |
| 90 | + ` resolve: {alias: {'@fixture': ${JSON.stringify(path.join(project.root, 'src', 'fixture'))}}},`, |
| 91 | + '})', |
| 92 | + '', |
| 93 | + ].join('\n'), |
| 94 | + ) |
| 95 | + |
| 96 | + await writeProjectFile( |
| 97 | + project.root, |
| 98 | + 'src/fixture/message.ts', |
| 99 | + "export const message = 'alias-hit'\n", |
| 100 | + ) |
| 101 | + |
| 102 | + await writeProjectFile( |
| 103 | + project.root, |
| 104 | + 'src/style.css', |
| 105 | + [ |
| 106 | + ':root {', |
| 107 | + " --fixture-color: #123456;", |
| 108 | + '}', |
| 109 | + '.fixture {', |
| 110 | + ' color: var(--fixture-color);', |
| 111 | + '}', |
| 112 | + '', |
| 113 | + ].join('\n'), |
| 114 | + ) |
| 115 | + |
| 116 | + await writeProjectFile( |
| 117 | + project.root, |
| 118 | + 'src/main.ts', |
| 119 | + [ |
| 120 | + 'declare const process: {env: {API_URL?: string}}', |
| 121 | + 'declare const __EMP_TEST_ENV__: string', |
| 122 | + '', |
| 123 | + "import {message} from '@fixture/message'", |
| 124 | + "import './style.css'", |
| 125 | + '', |
| 126 | + 'const mount = document.getElementById("custom-root")', |
| 127 | + 'if (!mount) throw new Error("mount missing")', |
| 128 | + 'mount.textContent = [', |
| 129 | + " 'title:' + document.title,", |
| 130 | + " 'env:' + process.env.API_URL,", |
| 131 | + " 'define:' + process.env.__EMP_TEST_ENV__,", |
| 132 | + ' message,', |
| 133 | + "].join('|')", |
| 134 | + '', |
| 135 | + ].join('\n'), |
| 136 | + ) |
| 137 | + |
| 138 | + const result = await runCli( |
| 139 | + [ |
| 140 | + 'build', |
| 141 | + '--env', |
| 142 | + 'prod', |
| 143 | + '--env-vars', |
| 144 | + 'API_URL=https://api.example.test', |
| 145 | + '--clearLog=false', |
| 146 | + ], |
| 147 | + project.root, |
| 148 | + 180000, |
| 149 | + ) |
| 150 | + |
| 151 | + expect(result.code).toBe(0) |
| 152 | + expect(result.stderr).not.toContain('Failed to compile') |
| 153 | + |
| 154 | + const htmlPath = path.join(project.root, 'dist-real', 'main.html') |
| 155 | + const html = await readFile(htmlPath, 'utf8') |
| 156 | + expect(html).toContain('<title>CLI Real Config</title>') |
| 157 | + expect(html).toContain('id="custom-root"') |
| 158 | + expect(html).toContain('/cdn/') |
| 159 | + |
| 160 | + const files = await listFiles(path.join(project.root, 'dist-real')) |
| 161 | + const jsFile = files.find(file => file.startsWith('js/') && file.endsWith('.js')) |
| 162 | + const cssFile = files.find(file => file.startsWith('css/') && file.endsWith('.css')) |
| 163 | + const jsMapFile = files.find(file => file.startsWith('js/') && file.endsWith('.js.map')) |
| 164 | + const cssMapFile = files.find(file => file.startsWith('css/') && file.endsWith('.css.map')) |
| 165 | + |
| 166 | + expect(jsFile).toBeTruthy() |
| 167 | + expect(cssFile).toBeTruthy() |
| 168 | + expect(jsMapFile).toBeTruthy() |
| 169 | + expect(cssMapFile).toBeTruthy() |
| 170 | + |
| 171 | + const js = await readFile(path.join(project.root, 'dist-real', jsFile as string), 'utf8') |
| 172 | + const css = await readFile(path.join(project.root, 'dist-real', cssFile as string), 'utf8') |
| 173 | + |
| 174 | + expect(js).toContain('alias-hit') |
| 175 | + expect(js).toContain('from-define') |
| 176 | + expect(js).toContain('https://api.example.test') |
| 177 | + expect(js).not.toContain('process.env.API_URL') |
| 178 | + expect(css).toContain('--fixture-color') |
| 179 | + |
| 180 | + const indexMap = await findFirst(path.join(project.root, 'dist-real'), file => file.endsWith('.map')) |
| 181 | + expect(indexMap).toBeTruthy() |
| 182 | + expect(await pathExists(path.join(project.root, 'dist-real', indexMap as string))).toBe(true) |
| 183 | + } finally { |
| 184 | + await project.cleanup() |
| 185 | + } |
| 186 | + }) |
| 187 | +}) |
0 commit comments