@coreframe/vite 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/dist/assets.d.ts +13 -0
- package/dist/assets.js +83 -0
- package/dist/image-generator.d.ts +18 -0
- package/dist/image-generator.js +79 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +34 -0
- package/dist/markdown-frontmatter.d.ts +11 -0
- package/dist/markdown-frontmatter.js +20 -0
- package/package.json +59 -0
package/dist/assets.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
|
|
3
|
+
//#region assets.d.ts
|
|
4
|
+
type CoreframeAssetsPluginOptions = {
|
|
5
|
+
devMaterialize?: boolean;
|
|
6
|
+
cwd?: string;
|
|
7
|
+
};
|
|
8
|
+
declare function coreframeAssetsPlugin({
|
|
9
|
+
cwd,
|
|
10
|
+
devMaterialize
|
|
11
|
+
}?: CoreframeAssetsPluginOptions): Plugin;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { CoreframeAssetsPluginOptions, coreframeAssetsPlugin };
|
package/dist/assets.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { checkAssets, formatAssetProblems, materializeAsset, readAssetManifest } from "@coreframe/scripts/assets";
|
|
2
|
+
|
|
3
|
+
//#region assets.ts
|
|
4
|
+
const manifestPath = "assets/manifest.json";
|
|
5
|
+
const markdownAssetReferencePattern = /@asset\s+([a-z0-9][a-z0-9/-]*)/g;
|
|
6
|
+
function coreframeAssetsPlugin({ cwd = process.cwd(), devMaterialize = true } = {}) {
|
|
7
|
+
const virtualId = "virtual:assets";
|
|
8
|
+
const resolvedVirtualId = `\0${virtualId}`;
|
|
9
|
+
return {
|
|
10
|
+
name: "coreframe:assets",
|
|
11
|
+
async buildStart() {
|
|
12
|
+
if (this.meta.watchMode) return;
|
|
13
|
+
const status = await checkAssets({ cwd });
|
|
14
|
+
if (!status.ok) throw new Error(formatAssetProblems(status.problems));
|
|
15
|
+
},
|
|
16
|
+
async resolveId(id) {
|
|
17
|
+
if (id === virtualId) return resolvedVirtualId;
|
|
18
|
+
},
|
|
19
|
+
async load(id) {
|
|
20
|
+
if (id !== resolvedVirtualId) return;
|
|
21
|
+
const { manifest, problems } = await readAssetManifest(cwd);
|
|
22
|
+
if (problems.length > 0) throw new Error(formatAssetProblems(problems));
|
|
23
|
+
return [
|
|
24
|
+
`const manifest = ${JSON.stringify(manifest)};`,
|
|
25
|
+
"export { manifest };",
|
|
26
|
+
"export function asset(id) {",
|
|
27
|
+
" const entry = manifest[id];",
|
|
28
|
+
" if (!entry) throw new Error(`Unknown Coreframe asset: ${id}`);",
|
|
29
|
+
" return entry;",
|
|
30
|
+
"}"
|
|
31
|
+
].join("\n");
|
|
32
|
+
},
|
|
33
|
+
async transform(code, id) {
|
|
34
|
+
const assetIds = [...code.matchAll(markdownAssetReferencePattern)].map((match) => match[1]);
|
|
35
|
+
if (assetIds.length === 0) return;
|
|
36
|
+
const loaded = await readAssetManifest(cwd);
|
|
37
|
+
const problems = [...loaded.problems];
|
|
38
|
+
let transformed = code;
|
|
39
|
+
for (const assetId of assetIds) {
|
|
40
|
+
const entry = loaded.manifest[assetId];
|
|
41
|
+
if (!entry) {
|
|
42
|
+
problems.push({
|
|
43
|
+
code: "INVALID_REFERENCE",
|
|
44
|
+
message: `Asset reference "${assetId}" in "${id}" is not present in ${manifestPath}.`,
|
|
45
|
+
assetId,
|
|
46
|
+
file: id
|
|
47
|
+
});
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (this.meta.watchMode && devMaterialize && entry.variants.length === 0) {
|
|
51
|
+
this.warn(`Materializing Coreframe asset "${assetId}" for dev.`);
|
|
52
|
+
const result = await materializeAsset(assetId, {
|
|
53
|
+
cwd,
|
|
54
|
+
updateManifest: false
|
|
55
|
+
});
|
|
56
|
+
problems.push(...result.problems);
|
|
57
|
+
const materialized = result.assets.find((asset) => asset.assetId === assetId);
|
|
58
|
+
if (materialized) loaded.manifest[assetId] = {
|
|
59
|
+
...entry,
|
|
60
|
+
variants: materialized.variants
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const variant = loaded.manifest[assetId]?.variants[0];
|
|
64
|
+
if (!variant) {
|
|
65
|
+
problems.push({
|
|
66
|
+
code: "GENERATED_VARIANT_MISSING",
|
|
67
|
+
message: `Asset "${assetId}" has no generated variant.`,
|
|
68
|
+
assetId,
|
|
69
|
+
file: id,
|
|
70
|
+
fix: `Run: coreframe assets build --id ${assetId}`
|
|
71
|
+
});
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
transformed = transformed.replaceAll(`@asset ${assetId}`, `/${variant.path}`);
|
|
75
|
+
}
|
|
76
|
+
if (problems.length > 0) throw new Error(formatAssetProblems(problems));
|
|
77
|
+
return transformed;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
//#endregion
|
|
83
|
+
export { coreframeAssetsPlugin };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ConfigEnv, FSWatcher } from "vite";
|
|
2
|
+
|
|
3
|
+
//#region image-generator.d.ts
|
|
4
|
+
declare function imageGenerator(): {
|
|
5
|
+
name: string;
|
|
6
|
+
configResolved(config: {
|
|
7
|
+
command: ConfigEnv["command"];
|
|
8
|
+
}): void;
|
|
9
|
+
configureServer(server: {
|
|
10
|
+
watcher: FSWatcher;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
applyToEnvironment(environment: {
|
|
13
|
+
name: string;
|
|
14
|
+
}): boolean;
|
|
15
|
+
buildStart(): Promise<void>;
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { imageGenerator };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { glob } from "tinyglobby";
|
|
4
|
+
|
|
5
|
+
//#region image-generator.ts
|
|
6
|
+
function imageGenerator() {
|
|
7
|
+
let command;
|
|
8
|
+
return {
|
|
9
|
+
name: "image-generator",
|
|
10
|
+
configResolved(config) {
|
|
11
|
+
command = config.command;
|
|
12
|
+
},
|
|
13
|
+
async configureServer(server) {
|
|
14
|
+
await generateAllDensityImages();
|
|
15
|
+
server.watcher.on("add", (file) => {
|
|
16
|
+
if (isSourceDensityImage(file)) generateDensityImages(file);
|
|
17
|
+
});
|
|
18
|
+
server.watcher.on("change", (file) => {
|
|
19
|
+
if (isSourceDensityImage(file)) generateDensityImages(file, { overwrite: true });
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
applyToEnvironment(environment) {
|
|
23
|
+
return environment.name === "client";
|
|
24
|
+
},
|
|
25
|
+
async buildStart() {
|
|
26
|
+
if (command === "build") await generateAllDensityImages();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async function generateAllDensityImages() {
|
|
31
|
+
const files = await glob(["src/**/*@3x.{png,webp}", "public/**/*@3x.{png,webp}"]);
|
|
32
|
+
console.log(`[image-generator] Found ${files.length} @3x images.`);
|
|
33
|
+
await Promise.all(files.map((file) => generateDensityImages(file)));
|
|
34
|
+
}
|
|
35
|
+
function isSourceDensityImage(file) {
|
|
36
|
+
return /@3x\.(png|webp)$/.test(file);
|
|
37
|
+
}
|
|
38
|
+
async function generateDensityImages(file, options = {}) {
|
|
39
|
+
const dir = path.dirname(file);
|
|
40
|
+
const ext = path.extname(file);
|
|
41
|
+
const base = path.basename(file, `@3x${ext}`);
|
|
42
|
+
const outputImages = [{
|
|
43
|
+
outputPath: path.join(dir, `${base}@2x${ext}`),
|
|
44
|
+
scale: 2 / 3
|
|
45
|
+
}, {
|
|
46
|
+
outputPath: path.join(dir, `${base}@1x${ext}`),
|
|
47
|
+
scale: 1 / 3
|
|
48
|
+
}].filter((image) => options.overwrite || !fs.existsSync(image.outputPath));
|
|
49
|
+
if (outputImages.length === 0) return;
|
|
50
|
+
const { default: sharp } = await import("sharp");
|
|
51
|
+
let width;
|
|
52
|
+
try {
|
|
53
|
+
width = (await sharp(file).metadata()).width;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error(`[image-generator] Failed to read metadata for ${file}`, error);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (!width) {
|
|
59
|
+
console.error(`[image-generator] Skipping ${file}; image width could not be read.`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
await Promise.all(outputImages.map((image) => generateDensityImage({
|
|
63
|
+
file,
|
|
64
|
+
outputPath: image.outputPath,
|
|
65
|
+
sharp,
|
|
66
|
+
width: Math.round(width * image.scale)
|
|
67
|
+
})));
|
|
68
|
+
}
|
|
69
|
+
async function generateDensityImage(options) {
|
|
70
|
+
try {
|
|
71
|
+
console.log(`[image-generator] Generating ${options.outputPath} (${options.width}w)`);
|
|
72
|
+
await options.sharp(options.file).resize({ width: options.width }).toFile(options.outputPath);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error(`[image-generator] Failed to process ${options.file} for ${options.outputPath}`, error);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
//#endregion
|
|
79
|
+
export { imageGenerator };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ConfigEnv, PluginOption } from "vite";
|
|
2
|
+
|
|
3
|
+
//#region index.d.ts
|
|
4
|
+
type CoreframeRunner = "tauri" | "vitest" | "react-router-ssg" | null;
|
|
5
|
+
type CoreframeConfigEnv = ConfigEnv & {
|
|
6
|
+
runner: CoreframeRunner;
|
|
7
|
+
};
|
|
8
|
+
type PluginFilter = (env: CoreframeConfigEnv) => boolean;
|
|
9
|
+
type PluginSelector = (env: CoreframeConfigEnv) => PluginOption;
|
|
10
|
+
type SelectablePlugin = PluginOption | PluginSelector;
|
|
11
|
+
declare function extendConfigEnv(env: ConfigEnv, processEnv?: NodeJS.ProcessEnv): CoreframeConfigEnv;
|
|
12
|
+
declare function selectPlugins(env: CoreframeConfigEnv, plugins: readonly SelectablePlugin[]): PluginOption[];
|
|
13
|
+
declare function apply(filter: PluginFilter, plugin: SelectablePlugin): PluginSelector;
|
|
14
|
+
declare function buildOnly(plugin: SelectablePlugin): PluginSelector;
|
|
15
|
+
declare function devOnly(plugin: SelectablePlugin): PluginSelector;
|
|
16
|
+
declare function runnerOnly(runners: CoreframeRunner | readonly CoreframeRunner[], plugin: SelectablePlugin): PluginSelector;
|
|
17
|
+
declare function exceptRunner(runners: CoreframeRunner | readonly CoreframeRunner[], plugin: SelectablePlugin): PluginSelector;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { CoreframeConfigEnv, CoreframeRunner, PluginFilter, PluginSelector, SelectablePlugin, apply, buildOnly, devOnly, exceptRunner, extendConfigEnv, runnerOnly, selectPlugins };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region index.ts
|
|
2
|
+
function extendConfigEnv(env, processEnv = process.env) {
|
|
3
|
+
return {
|
|
4
|
+
...env,
|
|
5
|
+
runner: processEnv.TAURI_ENV_PLATFORM !== void 0 ? "tauri" : processEnv.VITEST === "true" ? "vitest" : processEnv.IS_RR_BUILD_REQUEST === "yes" ? "react-router-ssg" : null
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
function selectPlugins(env, plugins) {
|
|
9
|
+
return plugins.map((plugin) => selectPlugin(env, plugin));
|
|
10
|
+
}
|
|
11
|
+
function apply(filter, plugin) {
|
|
12
|
+
return (env) => filter(env) ? selectPlugin(env, plugin) : null;
|
|
13
|
+
}
|
|
14
|
+
function buildOnly(plugin) {
|
|
15
|
+
return apply((env) => env.command === "build", plugin);
|
|
16
|
+
}
|
|
17
|
+
function devOnly(plugin) {
|
|
18
|
+
return apply((env) => env.command === "serve", plugin);
|
|
19
|
+
}
|
|
20
|
+
function runnerOnly(runners, plugin) {
|
|
21
|
+
return apply((env) => includesRunner(runners, env.runner), plugin);
|
|
22
|
+
}
|
|
23
|
+
function exceptRunner(runners, plugin) {
|
|
24
|
+
return apply((env) => !includesRunner(runners, env.runner), plugin);
|
|
25
|
+
}
|
|
26
|
+
function selectPlugin(env, plugin) {
|
|
27
|
+
return typeof plugin === "function" ? plugin(env) : plugin;
|
|
28
|
+
}
|
|
29
|
+
function includesRunner(runners, runner) {
|
|
30
|
+
return (typeof runners === "string" || runners === null ? [runners] : runners).includes(runner);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
export { apply, buildOnly, devOnly, exceptRunner, extendConfigEnv, runnerOnly, selectPlugins };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
|
|
3
|
+
//#region markdown-frontmatter.d.ts
|
|
4
|
+
type MarkdownFrontmatterPluginOptions = {
|
|
5
|
+
contentDirectory: string;
|
|
6
|
+
};
|
|
7
|
+
declare function markdownFrontmatterPlugin({
|
|
8
|
+
contentDirectory
|
|
9
|
+
}: MarkdownFrontmatterPluginOptions): Plugin;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { MarkdownFrontmatterPluginOptions, markdownFrontmatterPlugin };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { normalizePath } from "vite";
|
|
2
|
+
|
|
3
|
+
//#region markdown-frontmatter.ts
|
|
4
|
+
const markdownFrontmatterPattern = /^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/;
|
|
5
|
+
function markdownFrontmatterPlugin({ contentDirectory }) {
|
|
6
|
+
const normalizedContentDirectory = `${normalizePath(contentDirectory).replace(/\/+$/, "")}/`;
|
|
7
|
+
return {
|
|
8
|
+
name: "coreframe:markdown-frontmatter",
|
|
9
|
+
enforce: "pre",
|
|
10
|
+
transform(code, id) {
|
|
11
|
+
const [path = "", query] = id.split("?");
|
|
12
|
+
const normalizedPath = normalizePath(path);
|
|
13
|
+
if (query || !normalizedPath.startsWith(normalizedContentDirectory) || !path.endsWith(".md")) return;
|
|
14
|
+
return code.replace(markdownFrontmatterPattern, "");
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
20
|
+
export { markdownFrontmatterPlugin };
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@coreframe/vite",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/airlock-labs/coreframe.git",
|
|
7
|
+
"directory": "vite"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/"
|
|
11
|
+
],
|
|
12
|
+
"type": "module",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./assets": {
|
|
19
|
+
"types": "./dist/assets.d.ts",
|
|
20
|
+
"default": "./dist/assets.js"
|
|
21
|
+
},
|
|
22
|
+
"./image-generator": {
|
|
23
|
+
"types": "./dist/image-generator.d.ts",
|
|
24
|
+
"default": "./dist/image-generator.js"
|
|
25
|
+
},
|
|
26
|
+
"./markdown-frontmatter": {
|
|
27
|
+
"types": "./dist/markdown-frontmatter.d.ts",
|
|
28
|
+
"default": "./dist/markdown-frontmatter.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"tinyglobby": "^0.2.17",
|
|
33
|
+
"@coreframe/scripts": "^0.1.11"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^26.1.0",
|
|
37
|
+
"sharp": "^0.34.5",
|
|
38
|
+
"tsdown": "0.22.3",
|
|
39
|
+
"typescript": "6.0.3",
|
|
40
|
+
"vite": "^8.1.0",
|
|
41
|
+
"vitest": "^4.1.9"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"sharp": "^0.34.5 || ^0.35.0",
|
|
45
|
+
"vite": "^8.0.0"
|
|
46
|
+
},
|
|
47
|
+
"peerDependenciesMeta": {
|
|
48
|
+
"sharp": {
|
|
49
|
+
"optional": true
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsdown",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
56
|
+
"format": "oxfmt",
|
|
57
|
+
"lint": "oxlint"
|
|
58
|
+
}
|
|
59
|
+
}
|