@skafform/vite-plugin 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/base.css +52 -0
- package/dist/index.js +174 -0
- package/package.json +29 -0
- package/types.d.ts +20 -0
package/base.css
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/* Skafform — contrat universel. Shippe avec le plugin. */
|
|
2
|
+
:root {
|
|
3
|
+
/* Couleurs */
|
|
4
|
+
--skafform-primary: #111111;
|
|
5
|
+
--skafform-primary-fg: #ffffff;
|
|
6
|
+
--skafform-primary-hover: #333333;
|
|
7
|
+
--skafform-background: #ffffff;
|
|
8
|
+
--skafform-foreground: #111111;
|
|
9
|
+
--skafform-muted: #f5f5f5;
|
|
10
|
+
--skafform-muted-fg: #666666;
|
|
11
|
+
--skafform-border: #eeeeee;
|
|
12
|
+
--skafform-error: #dc2626;
|
|
13
|
+
|
|
14
|
+
/* Typographie */
|
|
15
|
+
--skafform-font: system-ui, sans-serif;
|
|
16
|
+
--skafform-font-heading: system-ui, sans-serif;
|
|
17
|
+
--skafform-font-body: system-ui, sans-serif;
|
|
18
|
+
--skafform-font-size-xs: 0.75rem;
|
|
19
|
+
--skafform-font-size-sm: 0.875rem;
|
|
20
|
+
--skafform-font-size-base: 1rem;
|
|
21
|
+
--skafform-font-size-lg: 1.125rem;
|
|
22
|
+
--skafform-font-size-xl: 1.25rem;
|
|
23
|
+
--skafform-font-size-2xl: 1.5rem;
|
|
24
|
+
|
|
25
|
+
/* Espacements */
|
|
26
|
+
--skafform-spacing-xs: 4px;
|
|
27
|
+
--skafform-spacing-sm: 8px;
|
|
28
|
+
--skafform-spacing-md: 16px;
|
|
29
|
+
--skafform-spacing-lg: 24px;
|
|
30
|
+
--skafform-spacing-xl: 32px;
|
|
31
|
+
|
|
32
|
+
/* Bordures */
|
|
33
|
+
--skafform-radius: 6px;
|
|
34
|
+
--skafform-radius-sm: 4px;
|
|
35
|
+
--skafform-radius-md: 6px;
|
|
36
|
+
--skafform-radius-lg: 12px;
|
|
37
|
+
|
|
38
|
+
/* Ombres */
|
|
39
|
+
--skafform-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
40
|
+
--skafform-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
|
41
|
+
--skafform-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
|
42
|
+
|
|
43
|
+
/* Mise en page */
|
|
44
|
+
--skafform-navbar-height: 60px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
html, body {
|
|
48
|
+
background: var(--skafform-background);
|
|
49
|
+
color: var(--skafform-foreground);
|
|
50
|
+
font-family: var(--skafform-font);
|
|
51
|
+
margin: 0;
|
|
52
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { readFileSync, existsSync, readdirSync, writeFileSync } from "fs";
|
|
3
|
+
import { resolve, dirname } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
var VIRTUAL_CONFIG = "virtual:skafform/config";
|
|
7
|
+
var RESOLVED_CONFIG = "\0virtual:skafform/config";
|
|
8
|
+
var VIRTUAL_SERVER_INIT = "virtual:skafform/server-init";
|
|
9
|
+
var RESOLVED_SERVER_INIT = "\0virtual:skafform/server-init";
|
|
10
|
+
var VIRTUAL_NAV = "virtual:skafform/nav";
|
|
11
|
+
var RESOLVED_NAV = "\0virtual:skafform/nav";
|
|
12
|
+
var norm = (p) => p.replace(/\\/g, "/");
|
|
13
|
+
function loadBrickIndex(root) {
|
|
14
|
+
const bricksDir = resolve(root, "bricks");
|
|
15
|
+
const names = /* @__PURE__ */ new Set();
|
|
16
|
+
if (!existsSync(bricksDir)) return names;
|
|
17
|
+
for (const entry of readdirSync(bricksDir)) {
|
|
18
|
+
if (entry.startsWith("@")) {
|
|
19
|
+
const scopeDir = resolve(bricksDir, entry);
|
|
20
|
+
for (const pkg of readdirSync(scopeDir)) {
|
|
21
|
+
names.add(`${entry}/${pkg}`);
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
names.add(entry);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return names;
|
|
28
|
+
}
|
|
29
|
+
function resolveBrick(id, root, index) {
|
|
30
|
+
const parts = id.split("/");
|
|
31
|
+
let brickName;
|
|
32
|
+
let subPath = null;
|
|
33
|
+
if (id.startsWith("@")) {
|
|
34
|
+
brickName = parts.slice(0, 2).join("/");
|
|
35
|
+
subPath = parts.length > 2 ? parts.slice(2).join("/") : null;
|
|
36
|
+
} else {
|
|
37
|
+
brickName = parts[0];
|
|
38
|
+
subPath = parts.length > 1 ? parts.slice(1).join("/") : null;
|
|
39
|
+
}
|
|
40
|
+
if (!index.has(brickName)) return null;
|
|
41
|
+
const brickDir = resolve(root, "bricks", brickName);
|
|
42
|
+
const pkgPath = resolve(brickDir, "package.json");
|
|
43
|
+
if (!existsSync(pkgPath)) return null;
|
|
44
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
45
|
+
const exportKey = subPath ? `./${subPath}` : ".";
|
|
46
|
+
const exportEntry = pkg.exports?.[exportKey];
|
|
47
|
+
if (exportEntry) {
|
|
48
|
+
const entry = typeof exportEntry === "string" ? exportEntry : exportEntry.import ?? exportEntry.default;
|
|
49
|
+
if (entry) return resolve(brickDir, entry);
|
|
50
|
+
}
|
|
51
|
+
const fallback = resolve(brickDir, subPath ? `src/${subPath}/index.ts` : "src/index.ts");
|
|
52
|
+
if (existsSync(fallback)) return fallback;
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
function skafform() {
|
|
56
|
+
const baseCssPath = resolve(__dirname, "../base.css");
|
|
57
|
+
let root = process.cwd();
|
|
58
|
+
let brickIndex = /* @__PURE__ */ new Set();
|
|
59
|
+
let config = {};
|
|
60
|
+
function getThemeJson(theme) {
|
|
61
|
+
const childPath = resolve(root, `themes/${theme}/child/theme.json`);
|
|
62
|
+
if (!existsSync(childPath)) return {};
|
|
63
|
+
return JSON.parse(readFileSync(childPath, "utf-8"));
|
|
64
|
+
}
|
|
65
|
+
function readCss(path) {
|
|
66
|
+
return readFileSync(path, "utf-8").replace(/^/, "");
|
|
67
|
+
}
|
|
68
|
+
function generateTokensCss(tokens) {
|
|
69
|
+
const entries = Object.entries(tokens);
|
|
70
|
+
if (entries.length === 0) return "";
|
|
71
|
+
const vars = entries.map(([key, value]) => ` --skafform-${key}: ${value};`).join("\n");
|
|
72
|
+
return `:root {
|
|
73
|
+
${vars}
|
|
74
|
+
}`;
|
|
75
|
+
}
|
|
76
|
+
function getCssString() {
|
|
77
|
+
const themeJson = getThemeJson(config.theme);
|
|
78
|
+
const themeCssPath = resolve(root, `themes/${config.theme}/styles/theme.css`);
|
|
79
|
+
const base = readCss(baseCssPath);
|
|
80
|
+
const theme = existsSync(themeCssPath) ? readCss(themeCssPath) : "";
|
|
81
|
+
const tokens = generateTokensCss(themeJson.tokens ?? {});
|
|
82
|
+
return base + "\n" + theme + (tokens ? "\n" + tokens : "");
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
name: "skafform",
|
|
86
|
+
enforce: "pre",
|
|
87
|
+
configResolved(resolved) {
|
|
88
|
+
root = resolved.root;
|
|
89
|
+
config = JSON.parse(readFileSync(resolve(root, "skafform.config.json"), "utf-8"));
|
|
90
|
+
brickIndex = loadBrickIndex(root);
|
|
91
|
+
writeFileSync(resolve(root, "app/skafform-theme.css"), getCssString(), "utf-8");
|
|
92
|
+
},
|
|
93
|
+
resolveId(id, importer) {
|
|
94
|
+
if (id === VIRTUAL_CONFIG) return RESOLVED_CONFIG;
|
|
95
|
+
if (id === VIRTUAL_SERVER_INIT) return RESOLVED_SERVER_INIT;
|
|
96
|
+
if (id === VIRTUAL_NAV) return RESOLVED_NAV;
|
|
97
|
+
if (id.startsWith(".") && importer) {
|
|
98
|
+
const themeDir = norm(resolve(root, `themes/${config.theme}`));
|
|
99
|
+
const componentsDir = themeDir + "/components";
|
|
100
|
+
const childDir = themeDir + "/child";
|
|
101
|
+
const resolved = norm(resolve(norm(dirname(importer)), id));
|
|
102
|
+
if (resolved.startsWith(componentsDir)) {
|
|
103
|
+
const filename = resolved.slice(componentsDir.length + 1);
|
|
104
|
+
for (const ext of ["", ".tsx", ".ts", ".jsx", ".js"]) {
|
|
105
|
+
const childFile = resolve(childDir, filename + ext);
|
|
106
|
+
if (existsSync(childFile)) return childFile;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (id.startsWith("/") || id.startsWith("\0") || id.startsWith("node:")) return;
|
|
112
|
+
const brick = resolveBrick(id, root, brickIndex);
|
|
113
|
+
if (brick) return brick;
|
|
114
|
+
},
|
|
115
|
+
load(id) {
|
|
116
|
+
if (id === RESOLVED_CONFIG) {
|
|
117
|
+
const themeJson = getThemeJson(config.theme);
|
|
118
|
+
return `export default ${JSON.stringify({
|
|
119
|
+
...config,
|
|
120
|
+
customize: { ...themeJson.customize ?? {}, ...config.customize ?? {} }
|
|
121
|
+
})}`;
|
|
122
|
+
}
|
|
123
|
+
if (id === RESOLVED_NAV) {
|
|
124
|
+
const bricksConfigPath = resolve(root, "skafform-bricks.json");
|
|
125
|
+
const themeJson = getThemeJson(config.theme);
|
|
126
|
+
const navLocations = themeJson.nav_locations ?? config.nav_locations ?? {};
|
|
127
|
+
const customNav = themeJson.nav ?? config.nav ?? {};
|
|
128
|
+
const bricksConfig = existsSync(bricksConfigPath) ? JSON.parse(readFileSync(bricksConfigPath, "utf-8")) : { bricks: {} };
|
|
129
|
+
const registry = {};
|
|
130
|
+
for (const loc of Object.keys(navLocations)) registry[loc] = [];
|
|
131
|
+
for (const [brickName, brick] of Object.entries(bricksConfig.bricks ?? {})) {
|
|
132
|
+
for (const item of brick.nav ?? []) {
|
|
133
|
+
if (registry[item.location] !== void 0) {
|
|
134
|
+
registry[item.location].push({ ...item, order: item.order ?? 0, brick: brickName });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
for (const [loc, items] of Object.entries(customNav)) {
|
|
139
|
+
if (registry[loc] !== void 0) {
|
|
140
|
+
registry[loc].push(...items.map((i) => ({ ...i, order: i.order ?? 0, brick: "config" })));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
for (const loc of Object.keys(registry)) {
|
|
144
|
+
registry[loc].sort((a, b) => a.order - b.order);
|
|
145
|
+
}
|
|
146
|
+
return `export default ${JSON.stringify(registry)}`;
|
|
147
|
+
}
|
|
148
|
+
if (id === RESOLVED_SERVER_INIT) {
|
|
149
|
+
const bricksConfigPath = resolve(root, "skafform-bricks.json");
|
|
150
|
+
if (!existsSync(bricksConfigPath)) return "";
|
|
151
|
+
const bricksConfig = JSON.parse(readFileSync(bricksConfigPath, "utf-8"));
|
|
152
|
+
const adapters = bricksConfig.adapters ?? {};
|
|
153
|
+
if (!adapters.auth) return "";
|
|
154
|
+
const adapterPath = resolve(root, adapters.auth).replace(/\\/g, "/");
|
|
155
|
+
return [
|
|
156
|
+
`import { setAdapter } from "@skafform/core/runtime"`,
|
|
157
|
+
`import { authAdapter } from "${adapterPath}"`,
|
|
158
|
+
`setAdapter(authAdapter)`
|
|
159
|
+
].join("\n");
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
handleHotUpdate({ file, server }) {
|
|
163
|
+
if (file.includes("skafform.config") || file.includes("themes") || file.includes("bricks")) {
|
|
164
|
+
config = JSON.parse(readFileSync(resolve(root, "skafform.config.json"), "utf-8"));
|
|
165
|
+
brickIndex = loadBrickIndex(root);
|
|
166
|
+
server.moduleGraph.invalidateAll();
|
|
167
|
+
server.ws.send({ type: "full-reload" });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export {
|
|
173
|
+
skafform
|
|
174
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@skafform/vite-plugin",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"import": "./dist/index.js"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"types": "./types.d.ts",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"base.css",
|
|
14
|
+
"types.d.ts"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup",
|
|
18
|
+
"dev": "tsup --watch"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"vite": ">=5.0.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^25.9.2",
|
|
25
|
+
"tsup": "^8.0.0",
|
|
26
|
+
"typescript": "^5.0.0",
|
|
27
|
+
"vite": "^8.0.16"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare module "virtual:skafform/server-init" {}
|
|
2
|
+
|
|
3
|
+
interface SkafformNavItem {
|
|
4
|
+
label: string
|
|
5
|
+
href: string
|
|
6
|
+
visibility: "public" | "guest" | "authenticated" | "admin"
|
|
7
|
+
order: number
|
|
8
|
+
brick: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare module "virtual:skafform/nav" {
|
|
12
|
+
const nav: Record<string, SkafformNavItem[]>
|
|
13
|
+
export default nav
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare module "virtual:skafform/config" {
|
|
17
|
+
const config: Record<string, unknown>
|
|
18
|
+
export default config
|
|
19
|
+
}
|
|
20
|
+
|