|
| 1 | +import { describe, expect, it } from "vitest"; |
| 2 | +import type { Nitro } from "nitro/types"; |
| 3 | +import { |
| 4 | + NODE_MODULES_RE, |
| 5 | + libChunkName, |
| 6 | + pathToPkgName, |
| 7 | + getChunkName, |
| 8 | + routeToFsPath, |
| 9 | +} from "../../src/build/chunks.ts"; |
| 10 | + |
| 11 | +function createChunk(name: string, moduleIds: string[]): { name: string; moduleIds: string[] } { |
| 12 | + return { name, moduleIds }; |
| 13 | +} |
| 14 | + |
| 15 | +function createNitro(overrides: Partial<Nitro> = {}): Nitro { |
| 16 | + return { |
| 17 | + options: { buildDir: "/build", tasks: {}, ...overrides.options }, |
| 18 | + routing: { |
| 19 | + routes: { routes: [] }, |
| 20 | + ...overrides.routing, |
| 21 | + }, |
| 22 | + ...overrides, |
| 23 | + } as unknown as Nitro; |
| 24 | +} |
| 25 | + |
| 26 | +describe("NODE_MODULES_RE", () => { |
| 27 | + it.each([ |
| 28 | + ["/foo/node_modules/bar/index.js", true], |
| 29 | + ["node_modules/bar/index.js", true], |
| 30 | + ["node_modules\\bar\\index.js", true], |
| 31 | + ["/foo/node_modules/nitro/dist/index.js", false], |
| 32 | + ["/foo/node_modules/nitro-nightly/dist/index.js", false], |
| 33 | + ["/foo/node_modules/.nitro", false], |
| 34 | + ["/foo/node_modules/.cache", false], |
| 35 | + ["/foo/src/bar.js", false], |
| 36 | + ])("%s → %s", (path, expected) => { |
| 37 | + expect(NODE_MODULES_RE.test(path)).toBe(expected); |
| 38 | + }); |
| 39 | +}); |
| 40 | + |
| 41 | +describe("pathToPkgName", () => { |
| 42 | + it.each([ |
| 43 | + ["/foo/node_modules/express/index.js", "express"], |
| 44 | + ["/foo/node_modules/@h3/core/index.js", "@h3/core"], |
| 45 | + ["C:\\proj\\node_modules\\express\\index.js", "express"], |
| 46 | + ["C:\\proj\\node_modules\\@h3\\core\\index.js", "@h3\\core"], |
| 47 | + ["/node_modules/nitro-nightly/dist/index.js", "nitro"], |
| 48 | + ["/node_modules/a/node_modules/b/index.js", "b"], |
| 49 | + ["/foo/src/bar.js", undefined], |
| 50 | + ])("%s → %s", (path, expected) => { |
| 51 | + expect(pathToPkgName(path)).toBe(expected); |
| 52 | + }); |
| 53 | +}); |
| 54 | + |
| 55 | +describe("libChunkName", () => { |
| 56 | + it.each([ |
| 57 | + ["/node_modules/express/index.js", "_libs/express"], |
| 58 | + ["/node_modules/@h3/core/index.js", "_libs/@h3/core"], |
| 59 | + ["/src/utils/foo.ts", undefined], |
| 60 | + ["/node_modules/nitro-nightly/dist/index.js", "_libs/nitro"], |
| 61 | + ])("%s → %s", (id, expected) => { |
| 62 | + expect(libChunkName(id)).toBe(expected); |
| 63 | + }); |
| 64 | +}); |
| 65 | + |
| 66 | +describe("routeToFsPath", () => { |
| 67 | + it.each([ |
| 68 | + ["/api/hello", "api/hello"], |
| 69 | + ["/api/users/:id", "api/users/[id]"], |
| 70 | + ["/", "index"], |
| 71 | + ["/api/users/:id/posts/*", "api/users/[id]/posts/[...]"], |
| 72 | + ])("%s → %s", (route, expected) => { |
| 73 | + expect(routeToFsPath(route)).toBe(expected); |
| 74 | + }); |
| 75 | +}); |
| 76 | + |
| 77 | +describe("getChunkName", () => { |
| 78 | + const nitro = createNitro(); |
| 79 | + |
| 80 | + it.each<[string, { name: string; moduleIds: string[] }, string]>([ |
| 81 | + ["rolldown-runtime", createChunk("rolldown-runtime", []), "_runtime.mjs"], |
| 82 | + ["_ chunks are preserved", createChunk("_shared", ["/src/foo.ts"]), "_shared.mjs"], |
| 83 | + [ |
| 84 | + "all node_modules (sorted a-z)", |
| 85 | + createChunk("vendor", ["/node_modules/express/index.js", "/node_modules/h3/dist/index.mjs"]), |
| 86 | + "_libs/express+h3.mjs", |
| 87 | + ], |
| 88 | + [ |
| 89 | + "single node_modules package", |
| 90 | + createChunk("vendor", ["/node_modules/a/index.js"]), |
| 91 | + "_libs/a.mjs", |
| 92 | + ], |
| 93 | + [ |
| 94 | + "node_modules names exceed 30 chars", |
| 95 | + createChunk("_libs/vendor", [ |
| 96 | + "/node_modules/some-very-long-package-name/index.js", |
| 97 | + "/node_modules/another-very-long-name/index.js", |
| 98 | + ]), |
| 99 | + "_libs/vendor+[...].mjs", |
| 100 | + ], |
| 101 | + [ |
| 102 | + "3 node_modules sorted a-z", |
| 103 | + createChunk("vendor", [ |
| 104 | + "/node_modules/zod/index.js", |
| 105 | + "/node_modules/ab/index.js", |
| 106 | + "/node_modules/h3/dist/index.mjs", |
| 107 | + ]), |
| 108 | + "_libs/ab+h3+zod.mjs", |
| 109 | + ], |
| 110 | + [ |
| 111 | + "scoped packages use __ separator", |
| 112 | + createChunk("vendor", ["/node_modules/@h3/core/index.js", "/node_modules/defu/index.js"]), |
| 113 | + "_libs/defu+h3__core.mjs", |
| 114 | + ], |
| 115 | + ["empty moduleIds (vacuous every())", createChunk("my-chunk", []), "_libs/_.mjs"], |
| 116 | + [ |
| 117 | + "virtual:raw modules", |
| 118 | + createChunk("raw", ["\0virtual:raw:foo", "#virtual:raw:bar"]), |
| 119 | + "_raw/[name].mjs", |
| 120 | + ], |
| 121 | + ["all virtual modules", createChunk("virt", ["\0something", "#other"]), "_virtual/[name].mjs"], |
| 122 | + ["wasm modules", createChunk("wasm", ["/src/module.wasm"]), "_wasm/[name].mjs"], |
| 123 | + [ |
| 124 | + "vite/services modules", |
| 125 | + createChunk("ssr", ["/vite/services/component.js"]), |
| 126 | + "_ssr/[name].mjs", |
| 127 | + ], |
| 128 | + ["buildDir modules", createChunk("build", ["/build/generated.js"]), "_build/[name].mjs"], |
| 129 | + [ |
| 130 | + "mixed virtual + wasm", |
| 131 | + createChunk("mixed", ["\0virtual:something", "/src/module.wasm"]), |
| 132 | + "_wasm/[name].mjs", |
| 133 | + ], |
| 134 | + ["fallback to _chunks", createChunk("misc", ["/src/utils/helper.ts"]), "_chunks/[name].mjs"], |
| 135 | + ])("%s → %s", (_label, chunk, expected) => { |
| 136 | + expect(getChunkName(chunk, nitro)).toBe(expected); |
| 137 | + }); |
| 138 | + |
| 139 | + it("returns _routes/<path>.mjs for route handler", () => { |
| 140 | + const n = createNitro({ |
| 141 | + routing: { |
| 142 | + routes: { |
| 143 | + routes: [{ data: [{ route: "/api/hello", handler: "/src/routes/api/hello.ts" }] }], |
| 144 | + }, |
| 145 | + }, |
| 146 | + } as any); |
| 147 | + expect(getChunkName(createChunk("route", ["/src/routes/api/hello.ts"]), n)).toBe( |
| 148 | + "_routes/api/hello.mjs" |
| 149 | + ); |
| 150 | + }); |
| 151 | + |
| 152 | + it("returns _routes/<path>.mjs for dynamic route", () => { |
| 153 | + const n = createNitro({ |
| 154 | + routing: { |
| 155 | + routes: { |
| 156 | + routes: [ |
| 157 | + { |
| 158 | + data: [{ route: "/api/users/:id", handler: "/src/routes/api/users/[id].ts" }], |
| 159 | + }, |
| 160 | + ], |
| 161 | + }, |
| 162 | + }, |
| 163 | + } as any); |
| 164 | + expect(getChunkName(createChunk("route", ["/src/routes/api/users/[id].ts"]), n)).toBe( |
| 165 | + "_routes/api/users/[id].mjs" |
| 166 | + ); |
| 167 | + }); |
| 168 | + |
| 169 | + it("returns _routes/index.mjs for root route", () => { |
| 170 | + const n = createNitro({ |
| 171 | + routing: { |
| 172 | + routes: { |
| 173 | + routes: [{ data: [{ route: "/", handler: "/src/routes/index.ts" }] }], |
| 174 | + }, |
| 175 | + }, |
| 176 | + } as any); |
| 177 | + expect(getChunkName(createChunk("route", ["/src/routes/index.ts"]), n)).toBe( |
| 178 | + "_routes/index.mjs" |
| 179 | + ); |
| 180 | + }); |
| 181 | + |
| 182 | + it("returns _tasks/[name].mjs for task handler", () => { |
| 183 | + const n = createNitro({ |
| 184 | + options: { |
| 185 | + buildDir: "/build", |
| 186 | + tasks: { "db:migrate": { handler: "/src/tasks/migrate.ts" } }, |
| 187 | + }, |
| 188 | + } as any); |
| 189 | + expect(getChunkName(createChunk("task", ["/src/tasks/migrate.ts"]), n)).toBe( |
| 190 | + "_tasks/[name].mjs" |
| 191 | + ); |
| 192 | + }); |
| 193 | +}); |
0 commit comments