|
| 1 | +import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs"; |
| 2 | +import { tmpdir } from "node:os"; |
| 3 | +import { join } from "node:path"; |
| 4 | +import { build, createServer, type Plugin, type ViteDevServer } from "vite"; |
| 5 | +import { afterEach, expect, it } from "vitest"; |
| 6 | + |
| 7 | +import { appRootAlias } from "./app-root-alias.ts"; |
| 8 | + |
| 9 | +const roots: string[] = []; |
| 10 | +const servers: ViteDevServer[] = []; |
| 11 | + |
| 12 | +/** An app package with a `src` app root, plus a nested `lib` workspace package. */ |
| 13 | +function createProject() { |
| 14 | + const root = realpathSync.native(mkdtempSync(join(tmpdir(), "solid-start-app-root-alias-"))); |
| 15 | + roots.push(root); |
| 16 | + |
| 17 | + mkdirSync(join(root, "src")); |
| 18 | + mkdirSync(join(root, "lib/src"), { recursive: true }); |
| 19 | + writeFileSync(join(root, "package.json"), "{}"); |
| 20 | + writeFileSync(join(root, "lib/package.json"), "{}"); |
| 21 | + writeFileSync(join(root, "src/demo.ts"), 'export default "app";'); |
| 22 | + writeFileSync(join(root, "lib/src/demo.ts"), 'export default "package";'); |
| 23 | + |
| 24 | + return root; |
| 25 | +} |
| 26 | + |
| 27 | +afterEach(async () => { |
| 28 | + await Promise.all(servers.splice(0).map(server => server.close())); |
| 29 | + for (const root of roots.splice(0)) rmSync(root, { recursive: true }); |
| 30 | +}); |
| 31 | + |
| 32 | +it("resolves ~ for the app package, leaving other packages to importer-aware plugins", async () => { |
| 33 | + const root = createProject(); |
| 34 | + const packageRoot = join(root, "lib"); |
| 35 | + // Stands in for vite-tsconfig-paths: maps `~` to the importing package's root. |
| 36 | + const packagePaths: Plugin = { |
| 37 | + name: "test:package-paths", |
| 38 | + enforce: "pre", |
| 39 | + resolveId(id, importer, options) { |
| 40 | + if (id !== "~/demo" || !importer?.startsWith(packageRoot)) return null; |
| 41 | + return this.resolve(join(packageRoot, "src/demo"), importer, { ...options, skipSelf: true }); |
| 42 | + }, |
| 43 | + }; |
| 44 | + const server = await createServer({ |
| 45 | + configFile: false, |
| 46 | + logLevel: "silent", |
| 47 | + root, |
| 48 | + server: { middlewareMode: true }, |
| 49 | + plugins: [appRootAlias(root, "./src"), packagePaths], |
| 50 | + }); |
| 51 | + servers.push(server); |
| 52 | + |
| 53 | + await expect( |
| 54 | + server.pluginContainer.resolveId("~/demo", join(root, "src/app.tsx")), |
| 55 | + ).resolves.toMatchObject({ id: join(root, "src/demo.ts") }); |
| 56 | + await expect( |
| 57 | + server.pluginContainer.resolveId("~/demo", join(root, "lib/src/index.ts")), |
| 58 | + ).resolves.toMatchObject({ id: join(root, "lib/src/demo.ts") }); |
| 59 | +}); |
| 60 | + |
| 61 | +it("resolves ~ in stylesheets, which are resolved without user plugins", async () => { |
| 62 | + const root = createProject(); |
| 63 | + writeFileSync(join(root, "src/theme.css"), "body { color: red; }"); |
| 64 | + writeFileSync(join(root, "src/app.css"), '@import "~/theme.css";'); |
| 65 | + writeFileSync(join(root, "src/main.js"), 'import "./app.css";'); |
| 66 | + |
| 67 | + const result = (await build({ |
| 68 | + configFile: false, |
| 69 | + logLevel: "silent", |
| 70 | + root, |
| 71 | + plugins: [appRootAlias(root, "./src")], |
| 72 | + build: { write: false, rollupOptions: { input: join(root, "src/main.js") } }, |
| 73 | + })) as { output: { fileName: string; source?: unknown }[] }; |
| 74 | + |
| 75 | + const css = result.output.find(chunk => chunk.fileName.endsWith(".css")); |
| 76 | + expect(String(css?.source)).toContain("color:red"); |
| 77 | +}); |
0 commit comments