mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 06:05:24 +00:00
fix: put plugin-only tailwind classes in core's utilities layer, SDK 1.0.4
This commit is contained in:
1 parent
d6c4ec01b5
commit
878f1ebaa3
6 files changed
+169
-13
No files matched your search
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* dist/host/ui.js @termix-ssh/plugin-sdk/ui, the shell's UI kit
|
||||
* dist/host/testing-host.js what renderWithApp() renders plugins into
|
||||
* dist/host/core-classes.json the classes core's CSS has rules for
|
||||
*
|
||||
* Inside Termix the page's import map points @termix-ssh/plugin-sdk/ui at the
|
||||
* shell's own copy, so ui.js is never loaded there. It exists so a plugin in
|
||||
@@ -18,6 +19,9 @@ import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import * as esbuild from "esbuild";
|
||||
import { compile, optimize } from "@tailwindcss/node";
|
||||
import { Scanner } from "@tailwindcss/oxide";
|
||||
import { classNamesInCss } from "../packages/plugin-sdk/cli/lib/css-classes.mjs";
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const outDir = path.join(root, "packages", "plugin-sdk", "dist", "host");
|
||||
@@ -137,4 +141,30 @@ fs.copyFileSync(
|
||||
path.join(outDir, "theme.css"),
|
||||
);
|
||||
|
||||
// Every class core's own CSS has a rule for, found the way the Vite build
|
||||
// finds them, so termix-plugin build knows which plugin classes it shares.
|
||||
const indexCss = path.join(root, "src/ui/index.css");
|
||||
const compiler = await compile(fs.readFileSync(indexCss, "utf8"), {
|
||||
base: path.dirname(indexCss),
|
||||
onDependency: () => {},
|
||||
});
|
||||
const rootSource =
|
||||
compiler.root === "none"
|
||||
? []
|
||||
: compiler.root === null
|
||||
? [{ base: root, pattern: "**/*", negated: false }]
|
||||
: [{ ...compiler.root, negated: false }];
|
||||
const scanner = new Scanner({
|
||||
sources: [...rootSource, ...compiler.sources],
|
||||
});
|
||||
const coreClasses = [
|
||||
...classNamesInCss(
|
||||
optimize(compiler.build(scanner.scan()), { minify: true }).code,
|
||||
),
|
||||
].sort();
|
||||
fs.writeFileSync(
|
||||
path.join(outDir, "core-classes.json"),
|
||||
`${JSON.stringify(coreClasses)}\n`,
|
||||
);
|
||||
|
||||
console.log(`Built the SDK host modules into ${path.relative(root, outDir)}`);
|
||||
@@ -7,6 +7,10 @@ import {
|
||||
build,
|
||||
lowerPluginLayers,
|
||||
} from "../packages/plugin-sdk/cli/commands/build.mjs";
|
||||
import {
|
||||
classNamesInCss,
|
||||
splitByCoreClasses,
|
||||
} from "../packages/plugin-sdk/cli/lib/css-classes.mjs";
|
||||
|
||||
const cleanups: Array<() => void> = [];
|
||||
|
||||
@@ -102,6 +106,64 @@ describe("plugin CSS layers", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("can leave a plugin's own classes in core's utilities layer", () => {
|
||||
expect(
|
||||
lowerPluginLayers(
|
||||
"@layer properties{a{b:c}}@layer utilities{.w-px{width:1px}}",
|
||||
{ utilities: false },
|
||||
),
|
||||
).toBe(
|
||||
"@layer termix-plugin-properties{a{b:c}}@layer utilities{.w-px{width:1px}}",
|
||||
);
|
||||
});
|
||||
|
||||
it("reads class names from selectors, not declarations", () => {
|
||||
const names = classNamesInCss(
|
||||
String.raw`.w-28{width:calc(var(--spacing) * 28)}@media (width>=48rem){.md\:w-44{width:0.5rem}}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.top-1\/2{top:50%}.\32 xl\:flex{display:flex}`,
|
||||
);
|
||||
expect([...names].sort()).toEqual(
|
||||
[
|
||||
"2xl:flex",
|
||||
"group",
|
||||
"group-hover:opacity-100",
|
||||
"md:w-44",
|
||||
"top-1/2",
|
||||
"w-28",
|
||||
].sort(),
|
||||
);
|
||||
});
|
||||
|
||||
it("splits a plugin's classes by whether core ships them", () => {
|
||||
expect(
|
||||
splitByCoreClasses(
|
||||
["w-28", "md:w-44", "hidden"],
|
||||
new Set(["w-28", "hidden"]),
|
||||
),
|
||||
).toEqual({ shared: ["w-28", "hidden"], own: ["md:w-44"] });
|
||||
});
|
||||
|
||||
it("keeps classes core ships below core and the plugin's own beside core's", async () => {
|
||||
vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
// Core's class list comes from scanning this repo, this file included,
|
||||
// so the plugin-only class is put together here instead of written out.
|
||||
const width = ["w-", "[321px]"].join("");
|
||||
const dir = fixturePlugin();
|
||||
fs.mkdirSync(path.join(dir, "src", "frontend"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(dir, "src", "frontend", "index.tsx"),
|
||||
`export const a = <div className="hidden md:${width}" />;\n`,
|
||||
);
|
||||
|
||||
await build({ cwd: dir });
|
||||
|
||||
const css = fs.readFileSync(path.join(dir, "dist", "frontend.css"), "utf8");
|
||||
const lowered = css.slice(css.indexOf("@layer termix-plugin-utilities"));
|
||||
const own = css.slice(css.lastIndexOf("@layer utilities{"));
|
||||
expect(lowered).toMatch(/^@layer termix-plugin-utilities\{[^@]*\.hidden\{/);
|
||||
expect(own).toContain(String.raw`.md\:w-\[321px\]`);
|
||||
expect(own).not.toContain(".hidden{");
|
||||
});
|
||||
|
||||
it("is ordered in core's CSS between base and core's utilities", () => {
|
||||
const css = fs.readFileSync(
|
||||
path.join(__dirname, "..", "src", "ui", "index.css"),
|
||||
|
||||
Reference in new issue
Block a user