@coreframe/vite 0.1.0 → 0.1.2

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.js CHANGED
@@ -1,83 +1,2 @@
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 };
1
+ import{readFile as e}from"node:fs/promises";import{checkAssets as t,formatAssetProblems as n,materializeAsset as r,readAssetManifest as i,resolveAssetVariantFile as a}from"@coreframe/scripts/assets";const o=/!\[([^\]]*)\]\(@asset\s+([a-z0-9][a-z0-9/-]*)\)/g;function s({cwd:e=process.cwd(),devMaterialize:s=!0}={}){let d=`virtual:assets`,h=`\0${d}`,g=new Map,_=``;function v(e){return{...e,variants:[...e.variants].sort((e,t)=>e.width-t.width).map(e=>({...e,src:c(_,e.path)}))}}async function y(t){let i=g.get(t);if(i)return i;let a=(async()=>{let i=await r(t,{cwd:e,updateManifest:!1});if(!i.ok)throw Error(n(i.problems));return i})();g.set(t,a);try{return await a}finally{g.delete(t)}}return{name:`coreframe:assets`,configResolved(e){_=l(e.env.VITE_MEDIA_PUBLIC_BASE_URL)},configureServer(t){t.middlewares.use(async(r,o,c)=>{try{if(!r.url||![`GET`,`HEAD`].includes(r.method??`GET`)){c();return}let l=f(r.url);if(!l){c();return}let u=await i(e);if(u.problems.length>0){c(Error(n(u.problems)));return}let d=p(u.manifest,l);if(!d){c();return}let h=a(e,l),g=await m(h);if(!g&&s&&(t.config.logger.info(`Materializing Coreframe asset "${d}" for dev.`),await y(d),g=await m(h),!g))throw Error(`Coreframe asset variant "${l}" does not match its source. Run: coreframe assets build --id ${d}`);if(!g){c();return}o.statusCode=200,o.setHeader(`Content-Type`,`image/webp`),o.setHeader(`Cache-Control`,`public, max-age=31536000, immutable`),o.setHeader(`Content-Length`,g.byteLength),o.end(r.method===`HEAD`?void 0:g)}catch(e){c(e)}})},async buildStart(){if(this.meta.watchMode)return;let r=await t({cwd:e});if(!r.ok)throw Error(n(r.problems))},async resolveId(e){if(e===d)return h},async load(t){if(t!==h)return;let{manifest:r,problems:a}=await i(e);if(a.length>0)throw Error(n(a));let o=Object.fromEntries(Object.entries(r).map(([e,t])=>[e,v(t)]));return[`const manifest = ${JSON.stringify(o)};`,`export { manifest };`,`export function asset(id) {`,` const entry = manifest[id];`," if (!entry) throw new Error(`Unknown Coreframe asset: ${id}`);",` if (entry.variants.length === 0) {`," throw new Error(`Coreframe asset has no generated variants: ${id}. Run: coreframe assets build --id ${id}`);",` }`,` return entry;`,`}`].join(`
2
+ `)},async transform(t,r){let[a]=r.split(`?`,1);if(!a?.endsWith(`.md`)&&!a?.endsWith(`.mdx`))return;let c=[...t.matchAll(o)];if(c.length===0)return;let l=await i(e),d=[...l.problems],f=t;for(let e of c){let[t,n,i]=e,a=l.manifest[i];if(!a){d.push({code:`INVALID_REFERENCE`,message:`Asset reference "${i}" in "${r}" is not present in assets/manifest.json.`,assetId:i,file:r});continue}if(this.meta.watchMode&&s&&a.variants.length===0){this.warn(`Materializing Coreframe asset "${i}" for dev.`);let e=await y(i);d.push(...e.problems);let t=e.assets.find(e=>e.assetId===i);t&&(l.manifest[i]={...a,variants:t.variants})}let o=v(l.manifest[i]??a);if(o.variants.length===0){d.push({code:`GENERATED_VARIANT_MISSING`,message:`Asset "${i}" has no generated variants.`,assetId:i,file:r,fix:`Run: coreframe assets build --id ${i}`});continue}f=f.replace(t,u(n,o))}if(d.length>0)throw Error(n(d));return f}}}function c(e,t){return`${e}/${t}`}function l(e){return(e??``).replace(/\/+$/,``)}function u(e,t){let n=t.variants.at(-1),r=t.variants.map(e=>`${e.src} ${e.width}w`).join(`, `);return[`<img alt="${d(e)}"`,`src="${d(n.src)}"`,`srcset="${d(r)}"`,`sizes="100vw"`,`width="${t.width}"`,`height="${t.height}">`].join(` `)}function d(e){return e.replaceAll(`&`,`&amp;`).replaceAll(`"`,`&quot;`).replaceAll(`<`,`&lt;`).replaceAll(`>`,`&gt;`)}function f(e){let t=decodeURIComponent(new URL(e,`http://coreframe.local`).pathname).replace(/^\/+/,``);return t.startsWith(`assets/`)?t:void 0}function p(e,t){return Object.entries(e).find(([,e])=>e.variants.some(e=>e.path===t))?.[0]}async function m(t){try{return await e(t)}catch(e){if(h(e)&&e.code===`ENOENT`)return;throw e}}function h(e){return e instanceof Error&&`code`in e}export{s as coreframeAssetsPlugin};
@@ -0,0 +1,10 @@
1
+ import { Plugin } from "vite";
2
+
3
+ //#region icons.d.ts
4
+ type CoreframeIconsPluginOptions = {
5
+ /** Directory containing the flat, kebab-case `app` SVG icon collection. */appIconsDirectory?: string;
6
+ };
7
+ /** Provide on-demand React and `?sprite` icon imports backed by Iconify collections. */
8
+ declare function coreframeIconsPlugin(options?: CoreframeIconsPluginOptions): Plugin[];
9
+ //#endregion
10
+ export { CoreframeIconsPluginOptions, coreframeIconsPlugin };
package/dist/icons.js ADDED
@@ -0,0 +1,14 @@
1
+ import{readFile as e}from"node:fs/promises";import{createHash as t}from"node:crypto";import{isAbsolute as n,relative as r,resolve as i}from"node:path";import{buildParsedSVG as a,parseSVGContent as o}from"@iconify/utils";import{loadNodeIcon as s}from"@iconify/utils/lib/loader/node-loader";import c from"unplugin-icons/vite";const l=`src/assets/icons`,u=`\0coreframe:sprite-icon:`,d=`__COREFRAME_SPRITE_URL__`,f=/^[a-z0-9]+(?:-[a-z0-9]+)*$/;function p(t={}){let n=i(t.appIconsDirectory??l);async function r(t){if(f.test(t))try{let r=await e(i(n,`${t}.svg`),`utf8`);S(r,`app/${t}`);let s=o(r);if(!s||!a(s))throw Error(`Icon \`app/${t}\` must contain a valid viewBox`);return r}catch(e){if(O(e)&&e.code===`ENOENT`)return;throw e}}return[m({options:t,loadAppIcon:r,setAppIconsDirectory(e){n=e}}),c({compiler:`jsx`,jsx:`react`,customCollections:{app:r}})]}function m({options:e,loadAppIcon:t,setAppIconsDirectory:n}){let r=new Map,a=``,o,c;return{name:`coreframe:icons`,enforce:`pre`,configResolved(t){o=t,a=w(t.root,e.appIconsDirectory??l),n(a)},resolveId(e){let t=g(e);return t?`${u}${encodeURIComponent(JSON.stringify(t))}.js`:null},async load(e){let n=h(e);if(!n)return null;n.collection===`app`&&this.addWatchFile(i(a,`${n.icon}.svg`));let c=n.collection===`app`?await t(n.icon):await s(n.collection,n.icon,{addXmlNs:!1,autoInstall:!1,cwd:o.root,scale:1.2});if(!c)throw Error(`Icon \`${n.collection}/${n.icon}\` not found`);let l=_(c,n.collection,n.icon);return r.set(l.id,l),v(l,n.props,o.command===`serve`)},renderStart(){o.command!==`build`||r.size===0||(c=this.emitFile({type:`asset`,name:`coreframe-icons.svg`,source:y(r.values())}))},renderChunk(e){if(!e.includes(d))return null;if(!c)throw Error(`Coreframe sprite asset was not emitted before chunk rendering`);let t=b(o.base,this.getFileName(c));return e.replaceAll(d,t)},handleHotUpdate(e){if(T(a,e.file)){for(let t of e.server.moduleGraph.idToModuleMap.values())(t.id?.includes(`~icons/app/`)||t.id?.startsWith(u))&&e.server.moduleGraph.invalidateModule(t);return e.server.ws.send({type:`full-reload`}),[]}}}}function h(e){if(!e.startsWith(u)||!e.endsWith(`.js`))return null;let t=e.slice(23,-3);return JSON.parse(decodeURIComponent(t))}function g(e){let t=e.indexOf(`?`);if(t===-1)return null;let n=new URLSearchParams(e.slice(t+1));if(!n.has(`sprite`))return null;n.delete(`sprite`);let r=e.slice(0,t).replace(/^\/?(?:~icons|virtual:icons|virtual\/icons)\//,``).replace(/\.\w+$/,``).split(`/`);if(r.length!==2||r.some(e=>!f.test(e)))throw Error(`Invalid sprite icon import: ${e}`);let i=Object.create(null);return n.forEach((e,t)=>{if(t===`raw`)throw Error(`An icon import cannot use both ?sprite and ?raw`);if(t===`style`)throw Error(`Pass sprite icon styles through the component style prop`);i[C(t)]=e}),{collection:r[0],icon:r[1],props:i}}function _(e,n,r){S(e,`${n}/${r}`);let i=o(e),s=i&&a(i);if(!i||!s)throw Error(`Icon \`${n}/${r}\` must contain a valid viewBox`);let c=`coreframe-icon-${n}-${r}-${t(`sha256`).update(`${n}/${r}\0${s.attributes.viewBox}\0${s.body}`).digest(`hex`).slice(0,12)}`,l=x(s.body,c),u=i.attribs.preserveAspectRatio?` preserveAspectRatio="${E(i.attribs.preserveAspectRatio)}"`:``;return{id:c,markup:`<symbol id="${c}" viewBox="${E(s.attributes.viewBox)}"${u}>${l}</symbol>`,viewBox:s.attributes.viewBox,width:s.attributes.width,height:s.attributes.height}}function v(e,t,n){let r={...e.width?{width:e.width}:{},...e.height?{height:e.height}:{},...t},i=n?`\nregisterSpriteSymbol(${JSON.stringify({id:e.id,markup:e.markup})})\n`:``,a=n?`import { registerSpriteSymbol, SpriteIcon } from "@coreframe/lib/sprite-icon"`:`import { SpriteIcon } from "@coreframe/lib/sprite-icon"`,o=n?`#${e.id}`:`${d}#${e.id}`;return`${a}
2
+ import { createElement } from "react"
3
+ ${i}
4
+ const defaultProps = ${JSON.stringify(r)}
5
+
6
+ export default function CoreframeSpriteIcon(props) {
7
+ return createElement(SpriteIcon, {
8
+ ...defaultProps,
9
+ ...props,
10
+ href: "${o}",
11
+ viewBox: "${e.viewBox}",
12
+ })
13
+ }
14
+ `}function y(e){return`<svg xmlns="http://www.w3.org/2000/svg" id="coreframe-icon-sprite">${Array.from(e,e=>e.markup).join(``)}</svg>`}function b(e,t){return`${e.endsWith(`/`)?e:`${e}/`}${t}`}function x(e,t){let n=Array.from(e.matchAll(/\bid=(['"])(.*?)\1/g),e=>e[2]),r=e;for(let[e,i]of n.entries()){let n=D(i),a=`${t}-${e+1}`;r=r.replace(RegExp(`\\bid=(['"])${n}\\1`,`g`),`id="${a}"`).replace(RegExp(`(["#;])${n}(?=[)"']|\\.[a-z])`,`g`),`$1${a}`)}return r}function S(e,t){if(/<(?:script|foreignObject)\b/i.test(e)||/\son[a-z]+\s*=/i.test(e))throw Error(`Icon \`${t}\` contains executable SVG content`);if(/\b(?:href|xlink:href)\s*=\s*['"](?!#)/i.test(e))throw Error(`Icon \`${t}\` contains an external SVG reference`)}function C(e){return e===`class`?`className`:e.startsWith(`aria-`)||e.startsWith(`data-`)?e:e.replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}function w(e,t){return n(t)?t:i(e,t)}function T(e,t){let i=r(e,t);return i!==``&&!i.startsWith(`..`)&&!n(i)}function E(e){return e.replaceAll(`&`,`&amp;`).replaceAll(`"`,`&quot;`)}function D(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function O(e){return e instanceof Error&&`code`in e}export{p as coreframeIconsPlugin};
package/dist/index.js CHANGED
@@ -1,34 +1 @@
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 };
1
+ function e(e,t=process.env){return{...e,runner:t.TAURI_ENV_PLATFORM===void 0?t.VITEST===`true`?`vitest`:t.IS_RR_BUILD_REQUEST===`yes`?`react-router-ssg`:null:`tauri`}}function t(e,t){return t.map(t=>s(e,t))}function n(e,t){return n=>e(n)?s(n,t):null}function r(e){return n(e=>e.command===`build`,e)}function i(e){return n(e=>e.command===`serve`,e)}function a(e,t){return n(t=>c(e,t.runner),t)}function o(e,t){return n(t=>!c(e,t.runner),t)}function s(e,t){return typeof t==`function`?t(e):t}function c(e,t){return(typeof e==`string`||e===null?[e]:e).includes(t)}export{n as apply,r as buildOnly,i as devOnly,o as exceptRunner,e as extendConfigEnv,a as runnerOnly,t as selectPlugins};
@@ -0,0 +1,12 @@
1
+ import { Plugin } from "vite";
2
+
3
+ //#region managed-dev.d.ts
4
+ type CoreframeManagedDevPluginOptions = {
5
+ env?: NodeJS.ProcessEnv;
6
+ };
7
+ declare function coreframeManagedDevPlugin({
8
+ env
9
+ }?: CoreframeManagedDevPluginOptions): Plugin;
10
+ declare const browserClientSource: string;
11
+ //#endregion
12
+ export { CoreframeManagedDevPluginOptions, browserClientSource, coreframeManagedDevPlugin };
@@ -0,0 +1,152 @@
1
+ import{managedDevTokenEnv as e,managedDevUrlEnv as t}from"@coreframe/scripts/managed-dev-protocol";const n=`/@coreframe/managed-dev`,r=`/__coreframe/managed-dev`;function i({env:i=process.env}={}){let u=i[t],d=i[e],f=!!(u&&d);async function p(e,t){if(!u||!d)throw Error(`Managed development is not configured.`);return fetch(new URL(e,u),{...t,headers:{...t?.headers,Authorization:`Bearer ${d}`,"Content-Type":`application/json`}})}return{name:`coreframe:managed-dev`,apply:`serve`,async configureServer(e){if(!f)return;let t=await p(`/handshake`,{method:`POST`});if(!t.ok){let e=await t.text();throw Error(`Coreframe managed development could not start: ${e}`)}e.middlewares.use(async(e,t,i)=>{if(!e.url){i();return}let u=new URL(e.url,`http://coreframe.local`).pathname;if(u===n){t.statusCode=200,t.setHeader(`Content-Type`,`text/javascript; charset=utf-8`),t.end(l);return}if(u===`${r}/status`&&e.method===`GET`){await a(await p(`/status`),t);return}if(u===`${r}/action`&&e.method===`POST`){if(!s(e.socket.remoteAddress)){c(t,403,{error:`Managed development actions require a loopback client.`});return}await a(await p(`/action`,{body:await o(e),method:`POST`}),t);return}i()})},transformIndexHtml(){if(f)return[{tag:`script`,attrs:{src:n,type:`module`},injectTo:`body`}]}}}async function a(e,t){let n=await e.text();t.statusCode=e.status,t.setHeader(`Content-Type`,e.headers.get(`content-type`)??`application/json`),t.end(n)}async function o(e){let t=``;for await(let n of e)if(t+=String(n),t.length>1e6)throw Error(`Managed development request is too large.`);return t}function s(e){return e===`127.0.0.1`||e===`::1`||e?.startsWith(`::ffff:127.`)}function c(e,t,n){e.statusCode=t,e.setHeader(`Content-Type`,`application/json`),e.end(JSON.stringify(n))}const l=String.raw`
2
+ const endpoint = "/__coreframe/managed-dev"
3
+ const channel = new BroadcastChannel("coreframe-managed-dev")
4
+ let status
5
+ let selected = new Set()
6
+
7
+ const host = document.createElement("div")
8
+ host.id = "coreframe-managed-dev"
9
+ host.setAttribute("aria-live", "polite")
10
+ document.body.append(host)
11
+ const root = host.attachShadow({ mode: "open" })
12
+
13
+ root.innerHTML = \`
14
+ <style>
15
+ :host { color-scheme: light dark; font: 14px/1.45 ui-sans-serif, system-ui, sans-serif; }
16
+ dialog { background: Canvas; color: CanvasText; border: 1px solid color-mix(in srgb, CanvasText 18%, transparent); border-radius: 14px; box-shadow: 0 18px 60px #0005; inset: auto 18px 18px auto; margin: 0; max-height: min(620px, calc(100vh - 36px)); max-width: min(430px, calc(100vw - 36px)); overflow: auto; padding: 18px; width: 100%; }
17
+ dialog[open] { display: grid; gap: 12px; }
18
+ h2 { font-size: 16px; margin: 0; }
19
+ p { margin: 0; }
20
+ .files { display: grid; gap: 7px; max-height: 190px; overflow: auto; }
21
+ label { align-items: start; display: flex; gap: 8px; overflow-wrap: anywhere; }
22
+ textarea { box-sizing: border-box; min-height: 92px; resize: vertical; width: 100%; }
23
+ .actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: end; }
24
+ button { border: 1px solid color-mix(in srgb, CanvasText 22%, transparent); border-radius: 8px; cursor: pointer; font: inherit; padding: 7px 11px; }
25
+ button.primary { background: #2563eb; border-color: #2563eb; color: white; }
26
+ button:disabled { cursor: wait; opacity: .55; }
27
+ .warning { color: #b45309; white-space: pre-wrap; }
28
+ .error { color: #dc2626; white-space: pre-wrap; }
29
+ </style>
30
+ <dialog aria-labelledby="coreframe-managed-title">
31
+ <h2 id="coreframe-managed-title">Coreframe update</h2>
32
+ <div class="content"></div>
33
+ </dialog>
34
+ \`
35
+
36
+ const dialog = root.querySelector("dialog")
37
+ const content = root.querySelector(".content")
38
+
39
+ channel.onmessage = () => void loadStatus()
40
+ setInterval(loadStatus, 2000)
41
+ void loadStatus()
42
+
43
+ async function loadStatus() {
44
+ try {
45
+ const response = await fetch(endpoint + "/status")
46
+ if (!response.ok) return
47
+ status = (await response.json()).status
48
+ render()
49
+ } catch {}
50
+ }
51
+
52
+ async function act(action) {
53
+ const response = await fetch(endpoint + "/action", {
54
+ method: "POST",
55
+ headers: { "Content-Type": "application/json" },
56
+ body: JSON.stringify(action),
57
+ })
58
+ const result = await response.json()
59
+ status = response.ok
60
+ ? result.status
61
+ : { ...(result.status || status), message: result.error }
62
+ channel.postMessage("changed")
63
+ render()
64
+ }
65
+
66
+ function render() {
67
+ if (!status || status.mode === "waiting" || (status.mode === "managed" && !status.target && !status.busy && !status.message && status.warnings.length === 0)) {
68
+ dialog.close()
69
+ return
70
+ }
71
+ if (!dialog.open) dialog.show()
72
+ const target = status.target
73
+ const draft = status.draft
74
+ if (draft && selected.size === 0) selected = new Set(draft.untracked)
75
+ content.innerHTML = ""
76
+ content.append(paragraph(status.busy || status.message || modeMessage(status)))
77
+ if (target) content.append(paragraph(target.subject + " (" + target.shortCommit + ")"))
78
+ for (const warning of status.warnings || []) content.append(paragraph(warning, "warning"))
79
+ if (status.mode === "error" && status.message) content.lastElementChild?.classList.add("error")
80
+
81
+ if (draft) {
82
+ const files = document.createElement("div")
83
+ files.className = "files"
84
+ for (const file of draft.untracked) files.append(checkbox(file, file, selected.has(file)))
85
+ if (draft.untracked.length) files.append(checkbox("__all", "Include all untracked files", selected.size === draft.untracked.length))
86
+ content.append(files)
87
+ if (status.proposal !== undefined) {
88
+ const message = document.createElement("textarea")
89
+ message.value = status.proposal
90
+ message.setAttribute("aria-label", "Commit message")
91
+ content.append(message)
92
+ content.append(actions(
93
+ button("Reject", () => act({ type: "reject" })),
94
+ button("Commit and refresh", () => act({ type: "apply", target: target.commit, fingerprint: draft.fingerprint, untracked: [...selected], message: message.value }), true),
95
+ ))
96
+ } else {
97
+ content.append(actions(
98
+ button("Reject", () => act({ type: "reject" })),
99
+ button("Generate commit message", () => act({ type: "message", fingerprint: draft.fingerprint, untracked: [...selected] }), true),
100
+ ))
101
+ }
102
+ } else if (target && !status.busy) {
103
+ const label = target.source === "local" ? "Sync with main" : "Refresh"
104
+ content.append(actions(button(label, () => act({ type: "prepare", target: target.commit }), true)))
105
+ }
106
+ }
107
+
108
+ function checkbox(value, labelText, checked) {
109
+ const label = document.createElement("label")
110
+ const input = document.createElement("input")
111
+ input.type = "checkbox"
112
+ input.checked = checked
113
+ input.onchange = () => {
114
+ if (value === "__all") selected = input.checked ? new Set(status.draft.untracked) : new Set()
115
+ else if (input.checked) selected.add(value)
116
+ else selected.delete(value)
117
+ render()
118
+ }
119
+ label.append(input, document.createTextNode(labelText))
120
+ return label
121
+ }
122
+
123
+ function paragraph(text, className) {
124
+ const element = document.createElement("p")
125
+ element.textContent = text || ""
126
+ if (className) element.className = className
127
+ return element
128
+ }
129
+
130
+ function button(label, onclick, primary = false) {
131
+ const element = document.createElement("button")
132
+ element.textContent = label
133
+ element.disabled = Boolean(status.busy)
134
+ if (primary) element.className = "primary"
135
+ element.onclick = onclick
136
+ return element
137
+ }
138
+
139
+ function actions(...buttons) {
140
+ const element = document.createElement("div")
141
+ element.className = "actions"
142
+ element.append(...buttons)
143
+ return element
144
+ }
145
+
146
+ function modeMessage(value) {
147
+ if (value.mode === "unmanaged") return "Managed synchronization is unavailable for this session."
148
+ if (value.mode === "error") return "Managed synchronization stopped."
149
+ if (value.target?.source === "local") return "New changes are available in local main."
150
+ return "A newer main commit is ready."
151
+ }
152
+ `;export{l as browserClientSource,i as coreframeManagedDevPlugin};
@@ -1,20 +1 @@
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 };
1
+ import{normalizePath as e}from"vite";const t=/^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/;function n({contentDirectory:n}){let r=`${e(n).replace(/\/+$/,``)}/`;return{name:`coreframe:markdown-frontmatter`,enforce:`pre`,transform(n,i){let[a=``,o]=i.split(`?`),s=e(a);if(!(o||!s.startsWith(r)||!a.endsWith(`.md`)))return n.replace(t,``)}}}export{n as markdownFrontmatterPlugin};
@@ -0,0 +1,6 @@
1
+ import { Plugin } from "vite";
2
+
3
+ //#region preset.d.ts
4
+ declare function coreframeVitePreset(): Plugin[];
5
+ //#endregion
6
+ export { coreframeVitePreset };
package/dist/preset.js ADDED
@@ -0,0 +1 @@
1
+ import{coreframeAssetsPlugin as e}from"./assets.js";import{coreframeIconsPlugin as t}from"./icons.js";import{coreframeManagedDevPlugin as n}from"./managed-dev.js";function r(){return[e(),...t(),n()]}export{r as coreframeVitePreset};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreframe/vite",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/airlock-labs/coreframe.git",
@@ -19,36 +19,41 @@
19
19
  "types": "./dist/assets.d.ts",
20
20
  "default": "./dist/assets.js"
21
21
  },
22
- "./image-generator": {
23
- "types": "./dist/image-generator.d.ts",
24
- "default": "./dist/image-generator.js"
22
+ "./icons": {
23
+ "types": "./dist/icons.d.ts",
24
+ "default": "./dist/icons.js"
25
25
  },
26
26
  "./markdown-frontmatter": {
27
27
  "types": "./dist/markdown-frontmatter.d.ts",
28
28
  "default": "./dist/markdown-frontmatter.js"
29
+ },
30
+ "./managed-dev": {
31
+ "types": "./dist/managed-dev.d.ts",
32
+ "default": "./dist/managed-dev.js"
33
+ },
34
+ "./preset": {
35
+ "types": "./dist/preset.d.ts",
36
+ "default": "./dist/preset.js"
29
37
  }
30
38
  },
31
39
  "dependencies": {
32
- "tinyglobby": "^0.2.17",
33
- "@coreframe/scripts": "^0.1.11"
40
+ "@iconify/utils": "^3.1.4",
41
+ "@svgr/core": "^8.1.0",
42
+ "@svgr/plugin-jsx": "^8.1.0",
43
+ "unplugin-icons": "^23.0.1",
44
+ "@coreframe/lib": "^0.1.4",
45
+ "@coreframe/scripts": "^0.1.16"
34
46
  },
35
47
  "devDependencies": {
36
48
  "@types/node": "^26.1.0",
37
- "sharp": "^0.34.5",
38
49
  "tsdown": "0.22.3",
39
50
  "typescript": "6.0.3",
40
51
  "vite": "^8.1.0",
41
52
  "vitest": "^4.1.9"
42
53
  },
43
54
  "peerDependencies": {
44
- "sharp": "^0.34.5 || ^0.35.0",
45
55
  "vite": "^8.0.0"
46
56
  },
47
- "peerDependenciesMeta": {
48
- "sharp": {
49
- "optional": true
50
- }
51
- },
52
57
  "scripts": {
53
58
  "build": "tsdown",
54
59
  "test": "vitest run",
@@ -1,18 +0,0 @@
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 };
@@ -1,79 +0,0 @@
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 };