fix(vite): resolve @vue-tui/vite from CommonJS Vite configs (#243)
* fix(vite): resolve @vue-tui/vite from CommonJS Vite configs
When a consumer project is CommonJS — package.json "type": "commonjs", or
no "type" field at all (Node defaults to CommonJS) — Vite loads vite.config.ts
as CommonJS and resolves its imports under the `require` condition.
@vue-tui/vite's exports used an `import`-only conditions object with no
fallback, so nothing matched under `require` and Vite threw:
[plugin externalize-deps] Failed to resolve "@vue-tui/vite".
This package is ESM only but it was tried to load by `require`.
Switch the export to a bare string (plus "./package.json"), matching the
other @vue-tui/* packages and @vitejs/plugin-vue. A string export is
condition-agnostic, so it resolves under both `import` and `require`; Node
then loads the ESM file via require(ESM), which every supported Node
(>=22.18) provides. No CommonJS build is added — this mirrors how the Vite
plugin ecosystem ships today (all ESM-only).
Add a regression test (test/cjs-config.test.ts): a `type: commonjs` fixture
whose vite.config.ts imports @vue-tui/vite by name, loaded through the real
config loader via resolveConfig(). It fails with the exact #238 error on the
old exports and passes with this change.
* chore(lint): exclude test fixtures from linting
Test fixtures are test INPUTS, not shipped source, and some are deliberately
broken: the overlay / full-reload dev-server tests transiently overwrite a
fixture's app.vue with a syntax error (to exercise the error overlay / failed
HMR), then restore it. Because `vp run ci` runs lint concurrently with those
tests, a linter that read a fixture inside that broken window failed with a
spurious "Unexpected token" — a flaky race whose surfacing depended on test
timing. Add lint.ignorePatterns ["**/test/fixtures/**"] to remove the race
(and the wasted lint work on test data).
This commit is contained in:
@@ -10,10 +10,8 @@
|
|||||||
],
|
],
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": "./dist/index.mjs",
|
||||||
"types": "./dist/index.d.mts",
|
"./package.json": "./package.json"
|
||||||
"import": "./dist/index.mjs"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// Regression test for #238: @vue-tui/vite must be loadable from a CommonJS consumer project.
|
||||||
|
//
|
||||||
|
// When a consumer's nearest package.json is `type: commonjs` (or has no `type` field — Node
|
||||||
|
// defaults to CommonJS), Vite bundles+loads vite.config.ts as CommonJS and resolves its imports
|
||||||
|
// under the `require` condition. @vue-tui/vite once exported only an `import` condition with no
|
||||||
|
// fallback, so nothing matched under `require` and config loading threw:
|
||||||
|
//
|
||||||
|
// [plugin externalize-deps] Failed to resolve "@vue-tui/vite".
|
||||||
|
// This package is ESM only but it was tried to load by `require`.
|
||||||
|
//
|
||||||
|
// The fixture (test/fixtures/cjs-config) has its own `type: commonjs` package.json to force the
|
||||||
|
// CJS config path, and imports @vue-tui/vite BY NAME. We symlink the real package into the
|
||||||
|
// fixture's node_modules first, so the bare import resolves against the built package and this
|
||||||
|
// exercises its published exports map under `require` — exactly a real consumer's layout.
|
||||||
|
// resolveConfig() runs the real config loader (the code path that threw) without a server/build.
|
||||||
|
import { test, expect, beforeAll, afterAll } from "vite-plus/test";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { mkdirSync, rmSync, symlinkSync } from "node:fs";
|
||||||
|
import { resolveConfig } from "vite";
|
||||||
|
|
||||||
|
const root = fileURLToPath(new URL("./fixtures/cjs-config", import.meta.url));
|
||||||
|
const pkgRoot = fileURLToPath(new URL("../", import.meta.url)); // packages/vite
|
||||||
|
const fixtureModules = `${root}/node_modules`;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
rmSync(fixtureModules, { recursive: true, force: true });
|
||||||
|
mkdirSync(`${fixtureModules}/@vue-tui`, { recursive: true });
|
||||||
|
// "junction" keeps this cross-platform (absolute dir link on Windows, plain symlink on POSIX).
|
||||||
|
symlinkSync(pkgRoot, `${fixtureModules}/@vue-tui/vite`, "junction");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
rmSync(fixtureModules, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
test("loads a config that imports @vue-tui/vite from a CommonJS project (#238)", async () => {
|
||||||
|
const config = await resolveConfig(
|
||||||
|
{ root, configFile: `${root}/vite.config.ts`, logLevel: "silent" },
|
||||||
|
"serve",
|
||||||
|
);
|
||||||
|
|
||||||
|
// The config resolved AND executed vueTui() under the CJS `require` path: its plugins are here.
|
||||||
|
expect(config.plugins.some((p) => p.name?.startsWith("vue-tui"))).toBe(true);
|
||||||
|
});
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// Fixture for the #238 regression test (test/cjs-config.test.ts).
|
||||||
|
//
|
||||||
|
// The sibling package.json declares `type: commonjs`, so Vite bundles+loads THIS config as
|
||||||
|
// CommonJS and resolves its imports under the `require` condition. Importing @vue-tui/vite BY
|
||||||
|
// NAME exercises the published package's exports map: if it isn't resolvable under `require`,
|
||||||
|
// config loading throws "This package is ESM only but it was tried to load by `require`".
|
||||||
|
import { vueTui } from "@vue-tui/vite";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
plugins: [vueTui()],
|
||||||
|
};
|
||||||
@@ -6,6 +6,13 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
fmt: {},
|
fmt: {},
|
||||||
lint: {
|
lint: {
|
||||||
|
// Don't lint test fixtures. They are test INPUTS, not shipped source, and some are
|
||||||
|
// deliberately broken: the overlay / full-reload dev-server tests transiently overwrite a
|
||||||
|
// fixture's app.vue with a syntax error (to exercise the error overlay / failed HMR), then
|
||||||
|
// restore it. Because `vp run ci` runs lint CONCURRENTLY with those tests, a linter that read
|
||||||
|
// a fixture inside that broken window failed with a spurious "Unexpected token" (a flaky race,
|
||||||
|
// surfaced by test timing). Excluding fixtures removes the race and the wasted lint work.
|
||||||
|
ignorePatterns: ["**/test/fixtures/**"],
|
||||||
options: { typeAware: true, typeCheck: false },
|
options: { typeAware: true, typeCheck: false },
|
||||||
rules: {
|
rules: {
|
||||||
// This is a terminal UI library: parsing keyboard escape sequences and
|
// This is a terminal UI library: parsing keyboard escape sequences and
|
||||||
|
|||||||
Reference in New Issue
Block a user