vue-zero-ai 1.1.3 → 1.2.1
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/README.md +3 -0
- package/dist/vue-zero.js +5 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,6 +59,8 @@ pages/
|
|
|
59
59
|
|
|
60
60
|
**Page titles** — Set `title: 'My Page'` and `document.title` updates on navigation.
|
|
61
61
|
|
|
62
|
+
**Error page** — `pages/error.vue` is auto-detected and rendered when a page fails to load (fetch or script errors).
|
|
63
|
+
|
|
62
64
|
**Design system** — Bootstrap 5 base with Notion-inspired `base.css` tokens (colors, typography, spacing, shadows). All rem-based for consistent scaling.
|
|
63
65
|
|
|
64
66
|
**CSP friendly** — Script evaluation uses Blob URL + dynamic import instead of `eval`/`new Function`. No `unsafe-eval` required.
|
|
@@ -148,6 +150,7 @@ VueZero.createApp({
|
|
|
148
150
|
2. **Options API only** — No `<script setup>`, no Composition API, no TypeScript
|
|
149
151
|
3. **No `<style scoped>`** — Use Bootstrap classes + `base.css` tokens. Use class names for isolation when needed.
|
|
150
152
|
4. **404 page** — `pages/404.vue` is auto-detected. Do NOT add it to `pages.json`
|
|
153
|
+
5. **Error page** — `pages/error.vue` is auto-detected. Do NOT add it to `pages.json`
|
|
151
154
|
|
|
152
155
|
## License
|
|
153
156
|
|
package/dist/vue-zero.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var VueZero=(()=>{var
|
|
1
|
+
var VueZero=(()=>{var E=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var Q=Object.getOwnPropertyNames;var Z=Object.prototype.hasOwnProperty;var ee=(n,e,t)=>e in n?E(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var te=(n,e)=>{for(var t in e)E(n,t,{get:e[t],enumerable:!0})},ne=(n,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Q(e))!Z.call(n,r)&&r!==t&&E(n,r,{get:()=>e[r],enumerable:!(s=Y(e,r))||s.enumerable});return n};var oe=n=>ne(E({},"__esModule",{value:!0}),n);var f=(n,e,t)=>ee(n,typeof e!="symbol"?e+"":e,t);var ue={};te(ue,{createApp:()=>pe});var j=class{constructor(e){f(this,"pagesDir");this.pagesDir=e.replace(/\/$/,"")}async scan(){let e=`${this.pagesDir}/pages.json`;try{let t=await fetch(e);if(!t.ok)throw new Error(`${t.status}`);let s=await t.json();return s.forEach(r=>{r.endsWith(".vue")&&console.error(`[vue-zero] pages.json entry should not include ".vue" extension (got: "${r}"). Use "${r.replace(/\.vue$/,"")}"`)}),s.map(r=>this.fileToRoute(r))}catch{return console.warn(`[vue-zero] pages.json not found at ${e}. Create it: ["index", "about", ...]`),[]}}fileToRoute(e){let t=e.replace(/\.vue$/,""),r=t.split("/").map(u=>this.segmentToPath(u)),a=r[r.length-1],c;a===""?(r.pop(),c=r.length===0?"/":"/"+r.join("/")):c="/"+r.join("/");let m=t.replace(/\//g,"-").replace(/[\[\]\.]/g,"").replace(/\.\.\./g,"");return{path:c,filePath:`${this.pagesDir}/${e.endsWith(".vue")?e:e+".vue"}`,name:m}}segmentToPath(e){return e.startsWith("[...")&&e.endsWith("]")?":"+e.slice(4,-1)+"*":e.startsWith("[")&&e.endsWith("]")?":"+e.slice(1,-1):e==="index"?"":e}};var C=class{constructor(){f(this,"injected",new Map);f(this,"activePageId",null)}inject(e,t,s="component"){if(!e.trim()||this.injected.has(t))return;let r=document.createElement("style");r.setAttribute("data-vue-zero",t),r.setAttribute("data-scope",s),r.textContent=e,document.head.appendChild(r),this.injected.set(t,{el:r,scope:s})}remove(e){let t=this.injected.get(e);t&&(t.el.remove(),this.injected.delete(e))}switchPage(e){for(let[t,s]of this.injected)s.scope==="page"&&(s.el.disabled=t!==e);this.activePageId=e}clearPages(){}clear(){this.injected.forEach(e=>e.el.remove()),this.injected.clear()}};var B=new C;function K(){return B}function re(n){let e={template:"",script:"",style:""},t=/<template(\s[^>]*)?>/.exec(n);if(t){let a=t.index+t[0].length,c=/<template(\s[^>]*)?>/gi,m=/<\/template>/gi,u=1,p=a;for(;p<n.length;){c.lastIndex=p,m.lastIndex=p;let b=c.exec(n),v=m.exec(n),w=b?b.index:1/0,y=v?v.index:1/0;if(y===1/0)break;if(w<y)u++,p=w+b[0].length;else{if(u--,u===0){e.template=n.slice(a,y).trim();break}p=y+11}}}let s=/<script(\s[^>]*)?>/.exec(n);if(s){let a=s.index+s[0].length,c=n.indexOf("<\/script>",a);c!==-1&&(e.script=n.slice(a,c).trim())}let r=/<style(\s[^>]*)?>/.exec(n);if(r){let a=r.index+r[0].length,c=n.indexOf("</style>",a);c!==-1&&(e.style=n.slice(a,c).trim())}return e}async function se(n){if(!n.trim())return{};let e=new Blob([n],{type:"text/javascript"}),t=URL.createObjectURL(e);try{return(await import(t)).default??{}}catch(s){throw new Error(`[vue-zero] script eval failed: ${s.message}`)}finally{URL.revokeObjectURL(t)}}async function P(n,e,t="component"){let s=await fetch(n);if(!s.ok)throw new Error(`[vue-zero] failed to fetch ${n} (${s.status})`);let r=await s.text(),a=re(r),c=a.template,m=a.script,u=a.style,p=await se(m);return u&&B.inject(u,e,t),{template:c,componentOptions:p,style:u}}var D=class{constructor(e){f(this,"componentsDir");f(this,"cache",new Map);this.componentsDir=e.replace(/\/$/,"")}async readManifest(){let e=`${this.componentsDir}/components.json`;try{let t=await fetch(e);if(!t.ok)throw new Error(`${t.status}`);return await t.json()}catch{return[]}}async loadAll(e){let t={};return await Promise.all(e.map(async s=>{try{t[s]=await this.load(s)}catch{console.error(`[vue-zero] components.json\uC5D0 "${s}"\uC774 \uB4F1\uB85D\uB418\uC5B4 \uC788\uC9C0\uB9CC \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${this.componentsDir}/${s}.vue`)}})),t}async load(e){if(this.cache.has(e))return this.cache.get(e);let t=`${this.componentsDir}/${e}.vue`,{template:s,componentOptions:r}=await P(t,`component-${e}`),a={...r,name:e,template:s};return this.cache.set(e,a),a}};function ae(n){let e=n.replace(/-/g,"+").replace(/_/g,"/"),t=e.padEnd(e.length+(4-e.length%4)%4,"="),s=atob(t),r=new Uint8Array(s.length);for(let a=0;a<s.length;a++)r[a]=s.charCodeAt(a);return new TextDecoder().decode(r)}var L=class{constructor(e={}){f(this,"config");this.config={enabled:e.enabled??!1,loginPage:e.loginPage??"/login",tokenKey:e.tokenKey??"token"}}isEnabled(){return this.config.enabled}isAuthenticated(){try{let e=localStorage.getItem(this.config.tokenKey);if(!e)return!1;let t=JSON.parse(ae(e.split(".")[1]));return t.exp&&t.exp*1e3<Date.now()?(localStorage.removeItem(this.config.tokenKey),!1):!0}catch{return!1}}getLoginPage(){return this.config.loginPage}};function ie(n){for(let e of["pagesDir","componentsDir","layoutsDir"]){let t=n[e];t!==void 0&&t.includes("\\")&&console.error(`[vue-zero] "${e}" must use "/" as separator (got: "${t}")`)}n.auth&&!n.auth.enabled&&(n.auth.loginPage||n.auth.tokenKey)&&console.warn('[vue-zero] auth options are set but "enabled" is false \u2014 auth guard will not run')}var ce=`
|
|
2
2
|
.vue-zero-progress {
|
|
3
3
|
position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 99999;
|
|
4
4
|
background: #42b883; transform-origin: left; transform: scaleX(0);
|
|
@@ -6,7 +6,8 @@ var VueZero=(()=>{var j=Object.defineProperty;var q=Object.getOwnPropertyDescrip
|
|
|
6
6
|
}
|
|
7
7
|
.vue-zero-progress.loading { transform: scaleX(0.8); transition: transform 8s cubic-bezier(0.1, 0.05, 0, 1); }
|
|
8
8
|
.vue-zero-progress.done { transform: scaleX(1); opacity: 0; transition: transform 0.1s ease, opacity 0.4s ease 0.1s; }
|
|
9
|
-
`;function
|
|
10
|
-
<component v-if="
|
|
9
|
+
`;function le(){let n=document.createElement("style");n.textContent=ce,document.head.appendChild(n);let e=document.createElement("div");return e.className="vue-zero-progress",document.body.appendChild(e),{start(){e.className="vue-zero-progress",e.offsetWidth,e.classList.add("loading")},done(){e.classList.remove("loading"),e.classList.add("done")}}}async function pe(n={}){ie(n);let e=(n.baseDir??"/").replace(/\/$/,""),t=e+"/"+(n.pagesDir??"pages").replace(/^\//,"").replace(/\/$/,""),s=e+"/"+(n.componentsDir??"components").replace(/^\//,"").replace(/\/$/,""),r=e+"/"+(n.layoutsDir??"layouts").replace(/^\//,"").replace(/\/$/,""),a=new L(n.auth),c=new D(s),m=new j(t),u=K(),p=le(),[b,v]=await Promise.all([c.readManifest(),m.scan()]),w=await c.loadAll(b);v.length===0&&console.warn('[vue-zero] No pages found. Create pages/pages.json: ["index", "about", ...]');let y=new Map,z=new Map,R=new Map,_=new Map,T=new Map,V=new Map,H=new Map(v.map(o=>[o.name,o]));async function x(o,i){if(y.has(o))return y.get(o);let l=await P(i,`page-${o}`,"page");return y.set(o,l),l}let X=new Set(["html","body","head","base","meta","link","style","script","noscript","template","slot","component","div","span","p","a","br","hr","img","input","button","select","option","textarea","form","label","fieldset","legend","table","thead","tbody","tfoot","tr","th","td","caption","colgroup","col","ul","ol","li","dl","dt","dd","h1","h2","h3","h4","h5","h6","header","footer","main","nav","section","article","aside","details","summary","dialog","figure","figcaption","video","audio","source","canvas","svg","iframe","embed","object","pre","code","blockquote","strong","em","small","sub","sup","mark"]);function $(o,i){if(z.has(o))return z.get(o);X.has(o)&&console.error(`[vue-zero] \uD398\uC774\uC9C0 \uD30C\uC77C\uBA85 "${o}"\uC740 HTML \uC608\uC57D \uD0DC\uADF8\uC640 \uCDA9\uB3CC\uD569\uB2C8\uB2E4. \uD30C\uC77C\uBA85\uC744 \uBCC0\uACBD\uD558\uC138\uC694.`);let l=Vue.defineComponent({...i.componentOptions,name:o,template:i.template,components:w});z.set(o,l);let d=i.componentOptions,g=d.layout;return _.set(o,g===!1?!1:g??"default"),T.set(o,d.title??null),V.set(o,d.auth!==!1),l}async function G(o){if(R.has(o))return R.get(o);let i=`${r}/${o}.vue`;try{let{template:l,componentOptions:d}=await P(i,`layout-${o}`,"layout"),g=l.replace(/<slot\s*\/>/,"<router-view />").replace(/<slot><\/slot>/,"<router-view />"),h=Vue.defineComponent({...d,name:`layout-${o}`,template:g,components:w});return R.set(o,h),h}catch{return console.warn(`[vue-zero] layout "${o}.vue" not found \u2014 rendering page without layout`),R.set(o,null),null}}let O=`${t}/404.vue`,N=(await fetch(O)).ok,W=`${t}/error.vue`,q=(await fetch(W)).ok,U=v.map(o=>({path:o.path,name:o.name,component:async()=>{let i=await x(o.name,o.filePath);return $(o.name,i)}}));N&&U.push({path:"/:pathMatch(.*)*",name:"404",component:async()=>{let o=await x("404",O);return $("404",o)}});let S=VueRouter.createRouter({history:VueRouter.createWebHistory(),routes:U,scrollBehavior(o,i,l){return l||{top:0}}}),A=Vue.shallowRef(null),I=Vue.ref("__pending__"),M=Vue.shallowRef(null);S.beforeEach(async o=>{p.start(),M.value=null;let i=String(o.name),l=H.get(i);try{if(l){let h=await x(l.name,l.filePath);$(i,h)}else if(i==="404"&&N){let h=await x("404",O);$("404",h)}let d=_.get(i);if(d===!1)A.value=null,I.value="__none__";else{let h=d??"default",J=await G(h);A.value=J,I.value=h}u.switchPage(`page-${i}`);let g=T.get(i);if(g&&(document.title=g),a.isEnabled()&&V.get(i)&&!a.isAuthenticated())return p.done(),a.getLoginPage()}catch(d){if(console.error(`[vue-zero] \uD398\uC774\uC9C0 \uB85C\uB4DC \uC2E4\uD328: ${o.fullPath}`,d),q)try{let g=await x("error",W);M.value=$("error",g)}catch(g){console.error("[vue-zero] error.vue \uB85C\uB4DC \uC2E4\uD328",g)}return p.done(),!1}}),S.afterEach(()=>{p.done()}),S.onError(o=>{console.error("[vue-zero] navigation error:",o)});let F=Vue.defineComponent({name:"LayoutWrapper",setup(){return{currentLayout:A,currentLayoutName:I,currentError:M}},template:`
|
|
10
|
+
<component v-if="currentError" :is="currentError" />
|
|
11
|
+
<component v-else-if="currentLayout" :is="currentLayout" :key="currentLayoutName" />
|
|
11
12
|
<router-view v-else-if="currentLayoutName !== '__pending__'" />
|
|
12
|
-
`}),k=Vue.createApp({template:"<LayoutWrapper />",components:{LayoutWrapper:
|
|
13
|
+
`}),k=Vue.createApp({template:"<LayoutWrapper />",components:{LayoutWrapper:F}});k.use(S),k.config.errorHandler=(o,i,l)=>{console.error(`[vue-zero] runtime error (${l}):`,o)},Object.entries(w).forEach(([o,i])=>{k.component(o,i)}),await S.isReady().catch(()=>{}),k.mount("#app")}return oe(ue);})();
|