@aerogel/vite 0.0.0-next.2fb667e321e0740b4db1a21bc948232a9bf219d6
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/aerogel-vite.cjs.js +2 -0
- package/dist/aerogel-vite.cjs.js.map +1 -0
- package/dist/aerogel-vite.d.ts +40 -0
- package/dist/aerogel-vite.esm.js +2 -0
- package/dist/aerogel-vite.esm.js.map +1 -0
- package/dist/virtual.d.ts +33 -0
- package/package.json +49 -0
- package/src/lib/404.ts +20 -0
- package/src/lib/html-eval.ts +72 -0
- package/src/lib/html.test.ts +76 -0
- package/src/lib/html.ts +35 -0
- package/src/lib/media-types.test.ts +14 -0
- package/src/lib/media-types.ts +25 -0
- package/src/lib/options.ts +18 -0
- package/src/lib/package-parser.ts +41 -0
- package/src/lib/solid.ts +87 -0
- package/src/main.ts +113 -0
- package/src/templates/404.html +56 -0
- package/src/testing/setup.ts +12 -0
- package/src/types/html-shims.d.ts +4 -0
- package/src/types/ts-reset.d.ts +1 -0
- package/src/types/virtual.d.ts +33 -0
- package/tsconfig.json +10 -0
- package/vite.config.ts +14 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("core-js/modules/esnext.async-iterator.map.js"),require("core-js/modules/esnext.iterator.map.js");var e=require("@vitejs/plugin-vue"),t=require("@noeldemartin/utils"),n=require("path"),i=require("vite-plugin-pwa"),r=require("mustache");require("core-js/modules/esnext.async-iterator.filter.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.filter.js");var o=require("fs");function s(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var a=s(e),l='<!DOCTYPE html>\n<html lang="en">\n <head>\n <meta charset="UTF-8" />\n <link rel="icon" href="/favicon.ico" />\n <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n <title>{{ app.name }}</title>\n <script>\n (function () {\n const basePath = \'{{{ app.basePath }}}\';\n const url = new URL(location.href);\n\n if (!url.pathname.startsWith(basePath)) {\n const link = document.createElement(\'link\');\n\n link.rel = \'stylesheet\';\n link.href = \'https://cdn.simplecss.org/simple.min.css\';\n\n document.head.appendChild(link);\n\n return;\n }\n\n const queryParameters = {};\n\n url.searchParams.forEach((value, key) => (queryParameters[key] = value));\n\n localStorage.setItem(\n \'static-404-redirect\',\n JSON.stringify({\n path: url.pathname.substring(basePath.length - 1),\n hash: url.hash,\n query: queryParameters,\n })\n );\n\n location.href = basePath;\n })();\n <\/script>\n\n <style>\n body {\n width: 100vw;\n height: 100vh;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n }\n </style>\n </head>\n <body>\n <h1>404 | Page not found</h1>\n <a href="/">go home</a>\n </body>\n</html>\n';function c(e,t){if(!e.baseUrl)return console.warn("It was not possible to create a Solid ClientID document because the baseUrl was not provided, to remove this warning add a `baseUrl` option in the Aerogel vite plugin options."),null;const n=e.baseUrl.endsWith("/")?e.baseUrl:`${e.baseUrl}/`,i=t.icons&&(t.icons["512x512"]??Object.values(t.icons).pop()),r={"@context":"https://www.w3.org/ns/solid/oidc-context.jsonld",client_id:`${n}clientid.jsonld`,client_name:e.name,redirect_uris:[n],client_uri:n,scope:"openid profile offline_access webid",grant_types:["refresh_token","authorization_code"],response_types:["code"]};return i&&(r.logo_uri=n+i),r}const p={apng:"image/apng",avif:"image/avif",bmp:"image/bmp",cur:"image/x-icon",gif:"image/gif",ico:"image/x-icon",jfif:"image/jpeg",jpeg:"image/jpeg",jpg:"image/jpeg",pjp:"image/jpeg",pjpeg:"image/jpeg",png:"image/png",svg:"image/svg",tif:"image/tiff",tiff:"image/tiff",webp:"image/webp"};function u(e){const t=e.match(/\.([^.]+)$/)?.[1];return(t&&p[t])??null}function m(e){return e.repository?"object"==typeof e.repository?e.repository.url.replace(`.${e.repository.type}`,""):e.repository?.replace("github:","https://github.com/"):null}function d(e){return Object.keys(e.dependencies??{}).filter((e=>e.startsWith("@aerogel/plugin-"))).map((e=>e.substring(16)))}function g(e,n){if(!o.existsSync(n))return;const i=JSON.parse(o.readFileSync(n).toString()),r=t.objectWithoutEmpty({sourceUrl:m(i),plugins:d(i)});Object.assign(e,r)}var h=Object.freeze({__proto__:null,css:function(e){return`<style>${this.readFile(e)}</style>`},favicons:function(){return`\n <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />\n <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />\n <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />\n <link rel="mask-icon" href="/safari-pinned-tab.svg" color="${(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}).maskIconColor??"#000000"}" />\n `},js:function(e){return`<script>${this.readFile(e)}<\/script>`},socialMeta:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const t=this.app.baseUrl&&`<meta property="og:url" content="${this.app.baseUrl}" />`,n=this.app.description&&`\n <meta property="og:description" content="${this.app.description}" />\n <meta name="description" content="${this.app.description}" />\n `,i=e.image&&`\n <meta property="og:image" content="${e.image}" />\n\n \x3c!-- TODO calculate real image dimensions (these are hard-coded) --\x3e\n <meta property="og:image:width" content="600" />\n <meta property="og:image:height" content="250" />\n `;return`\n <meta name="apple-mobile-web-app-title" content="${this.app.name}" />\n <meta name="application-name" content="${this.app.name}" />\n <meta name="msapplication-TileColor" content="${this.app.themeColor}" />\n <meta name="theme-color" content="${this.app.themeColor}" />\n <meta property="og:title" content="${this.app.name}" />\n <meta property="og:type" content="website" />\n ${t??""}\n ${n??""}\n ${i??""}\n `},splashJs:function(){return"\n <script>\n setTimeout(function() {\n if (!document.querySelector('#app.loading')) {\n return;\n }\n\n document.getElementById('splash').style.opacity = 1;\n }, 1000);\n <\/script>\n "},svg:function(e){return this.readFile(e)}});exports.AerogelResolver=function(){return{type:"component",resolve:e=>{if(e.startsWith("AG")&&!e.startsWith("AGStory"))return e.startsWith("AGSolid")?{name:e,as:e,from:"@aerogel/plugin-solid"}:e.startsWith("AGCloud")?{name:e,as:e,from:"@aerogel/plugin-offline-first"}:{name:e,as:e,from:"@aerogel/core"}}}},exports.default=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const s={name:e.name??"App",description:e.description,basePath:"/",baseUrl:e.baseUrl,themeColor:e.themeColor??"#ffffff"},p={"virtual:aerogel"(){const e={environment:process.env.NODE_ENV??"development",basePath:s.basePath,sourceUrl:s.sourceUrl};return`export default ${JSON.stringify(e)};`},"virtual:aerogel-solid":()=>function(e,n){const i=t.objectWithoutEmpty({clientID:c(e,n)});return`export default ${JSON.stringify(i)};`}(s,e)},m={name:"vite:aerogel",buildStart(e){Array.isArray(e.input)&&e.input[0]&&g(s,n.resolve(e.input[0],"../package.json"))},configureServer(t){t.watcher.on("ready",(()=>{s.baseUrl=t.resolvedUrls?.network?.[0]??t.resolvedUrls?.local?.[0]??s.baseUrl})),t.middlewares.use(function(e,t){return(n,i,r)=>{if(!n.url?.endsWith("/clientid.jsonld"))return void r();const o=c(e,t);o?(i.statusCode=200,i.setHeader("Content-Type","application/ld+json"),i.write(JSON.stringify(o),"utf-8"),i.end()):r()}}(s,e)),g(s,`${t.config.root}/package.json`)},config:e=>(s.basePath=e.base??s.basePath,e.optimizeDeps=e.optimizeDeps??{},e.optimizeDeps.exclude=[...e.optimizeDeps.exclude??[],...Object.keys(p)],e),generateBundle(){!function(e,t,n){(n.static404Redirect??"production"===process.env.NODE_ENV)&&e.emitFile({type:"asset",fileName:"string"==typeof n.static404Redirect?n.static404Redirect:"404.html",source:r.render(l,{app:t})})}(this,s,e),function(e,t,n){if(!t.plugins?.includes("solid"))return;const i=c(t,n);i&&e.emitFile({type:"asset",fileName:"clientid.jsonld",source:JSON.stringify(i)})}(this,s,e)},load:e=>p[e]?.(),resolveId:e=>e in p?e:void 0,transformIndexHtml:(e,i)=>function(e,i,r){const s=t.stringMatchAll(e,/{{(.*?)}}/g),a=n.dirname(i),l=e=>o.readFileSync(n.resolve(a,e)).toString();for(const n of s)e=e.replace(n[0],t.toString((c=n[1],p={app:r,readFile:l},Function(`with (this) { return ${c} }`).bind({...h,...p})())));var c,p;return e}(e,i.filename,s)};return[a.default(),i.VitePWA({registerType:"autoUpdate",manifest:t.objectWithoutEmpty({name:s.name,short_name:s.name,description:s.description,theme_color:e.themeColor,icons:e.icons&&Object.entries(e.icons).map((e=>{let[n,i]=e;return t.objectWithoutEmpty({src:i,sizes:n,type:u(i)})}))})}),m].flat()};
|
|
2
|
+
//# sourceMappingURL=aerogel-vite.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aerogel-vite.cjs.js","sources":["../src/lib/solid.ts","../src/lib/media-types.ts","../src/lib/package-parser.ts","../src/lib/html-eval.ts","../src/main.ts","../src/lib/404.ts","../src/lib/html.ts"],"sourcesContent":["import { objectWithoutEmpty } from '@noeldemartin/utils';\nimport type { Connect } from 'vite';\nimport type { PluginContext } from 'rollup';\n\nimport type { AppInfo, Options } from '@/lib/options';\n\nimport type { ClientIDDocument, VirtualAerogelSolid } from 'virtual:aerogel-solid';\n\nfunction createClientIDDocument(app: AppInfo, options: Options): ClientIDDocument | null {\n if (!app.baseUrl) {\n // eslint-disable-next-line no-console\n console.warn(\n 'It was not possible to create a Solid ClientID document because the baseUrl ' +\n 'was not provided, to remove this warning add a `baseUrl` option in the Aerogel vite plugin options.',\n );\n\n return null;\n }\n\n const baseUrl = app.baseUrl.endsWith('/') ? app.baseUrl : `${app.baseUrl}/`;\n const logoPublicPath = options.icons && (options.icons['512x512'] ?? Object.values(options.icons).pop());\n const clientID: ClientIDDocument = {\n '@context': 'https://www.w3.org/ns/solid/oidc-context.jsonld',\n 'client_id': `${baseUrl}clientid.jsonld`,\n 'client_name': app.name,\n 'redirect_uris': [baseUrl],\n 'client_uri': baseUrl,\n 'scope': 'openid profile offline_access webid',\n 'grant_types': ['refresh_token', 'authorization_code'],\n 'response_types': ['code'],\n };\n\n if (logoPublicPath) {\n clientID.logo_uri = baseUrl + logoPublicPath;\n }\n\n return clientID;\n}\n\nexport function generateSolidAssets(context: PluginContext, app: AppInfo, options: Options): void {\n if (!app.plugins?.includes('solid')) {\n return;\n }\n\n const clientID = createClientIDDocument(app, options);\n\n if (!clientID) {\n return;\n }\n\n context.emitFile({\n type: 'asset',\n fileName: 'clientid.jsonld',\n source: JSON.stringify(clientID),\n });\n}\n\nexport function generateSolidVirtualModule(app: AppInfo, options: Options): string {\n const virtual: VirtualAerogelSolid = objectWithoutEmpty({\n clientID: createClientIDDocument(app, options),\n });\n\n return `export default ${JSON.stringify(virtual)};`;\n}\n\nexport function solidMiddleware(app: AppInfo, options: Options): Connect.NextHandleFunction {\n return (request, response, next) => {\n if (!request.url?.endsWith('/clientid.jsonld')) {\n next();\n\n return;\n }\n\n const clientID = createClientIDDocument(app, options);\n\n if (!clientID) {\n next();\n\n return;\n }\n\n response.statusCode = 200;\n response.setHeader('Content-Type', 'application/ld+json');\n response.write(JSON.stringify(clientID), 'utf-8');\n response.end();\n };\n}\n","// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types#common_image_file_types\nconst mediaTypeByExtension: Record<string, string> = {\n apng: 'image/apng',\n avif: 'image/avif',\n bmp: 'image/bmp',\n cur: 'image/x-icon',\n gif: 'image/gif',\n ico: 'image/x-icon',\n jfif: 'image/jpeg',\n jpeg: 'image/jpeg',\n jpg: 'image/jpeg',\n pjp: 'image/jpeg',\n pjpeg: 'image/jpeg',\n png: 'image/png',\n svg: 'image/svg',\n tif: 'image/tiff',\n tiff: 'image/tiff',\n webp: 'image/webp',\n};\n\nexport function guessMediaType(filename: string): string | null {\n const extension = filename.match(/\\.([^.]+)$/)?.[1];\n\n return (extension && mediaTypeByExtension[extension]) ?? null;\n}\n","import { existsSync, readFileSync } from 'fs';\nimport { objectWithoutEmpty } from '@noeldemartin/utils';\n\nimport type { AppInfo } from '@/lib/options';\n\ninterface PackageJson {\n repository?: string | { type: string; url: string };\n dependencies?: Record<string, string>;\n}\n\nfunction getSourceUrl(packageJson: PackageJson): string | null {\n if (!packageJson.repository) {\n return null;\n }\n\n if (typeof packageJson.repository === 'object') {\n return packageJson.repository.url.replace(`.${packageJson.repository.type}`, '');\n }\n\n return packageJson.repository?.replace('github:', 'https://github.com/');\n}\n\nfunction getPlugins(packageJson: PackageJson): string[] {\n return Object.keys(packageJson.dependencies ?? {})\n .filter((dependency) => dependency.startsWith('@aerogel/plugin-'))\n .map((plugin) => plugin.substring(16));\n}\n\nexport function loadPackageInfo(app: AppInfo, packageJsonPath: string): void {\n if (!existsSync(packageJsonPath)) {\n return;\n }\n\n const packageJson = JSON.parse(readFileSync(packageJsonPath).toString()) as PackageJson;\n const parsed: Partial<AppInfo> = objectWithoutEmpty({\n sourceUrl: getSourceUrl(packageJson),\n plugins: getPlugins(packageJson),\n });\n\n Object.assign(app, parsed);\n}\n","import type { AppInfo } from '@/lib/options';\n\nexport interface HTMLEvalScope {\n app: AppInfo;\n readFile(path: string): string;\n}\n\nexport function css(this: HTMLEvalScope, path: string): string {\n return `<style>${this.readFile(path)}</style>`;\n}\n\nexport function favicons(options: { maskIconColor?: string } = {}): string {\n return `\n <link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/apple-touch-icon.png\" />\n <link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicon-32x32.png\" />\n <link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/favicon-16x16.png\" />\n <link rel=\"mask-icon\" href=\"/safari-pinned-tab.svg\" color=\"${options.maskIconColor ?? '#000000'}\" />\n `;\n}\n\nexport function js(this: HTMLEvalScope, path: string): string {\n return `<script>${this.readFile(path)}</script>`;\n}\n\nexport function socialMeta(this: HTMLEvalScope, options: { image?: string } = {}): string {\n const urlMeta = this.app.baseUrl && `<meta property=\"og:url\" content=\"${this.app.baseUrl}\" />`;\n const descriptionMeta =\n this.app.description &&\n `\n <meta property=\"og:description\" content=\"${this.app.description}\" />\n <meta name=\"description\" content=\"${this.app.description}\" />\n `;\n const imageMeta =\n options.image &&\n `\n <meta property=\"og:image\" content=\"${options.image}\" />\n\n <!-- TODO calculate real image dimensions (these are hard-coded) -->\n <meta property=\"og:image:width\" content=\"600\" />\n <meta property=\"og:image:height\" content=\"250\" />\n `;\n\n return `\n <meta name=\"apple-mobile-web-app-title\" content=\"${this.app.name}\" />\n <meta name=\"application-name\" content=\"${this.app.name}\" />\n <meta name=\"msapplication-TileColor\" content=\"${this.app.themeColor}\" />\n <meta name=\"theme-color\" content=\"${this.app.themeColor}\" />\n <meta property=\"og:title\" content=\"${this.app.name}\" />\n <meta property=\"og:type\" content=\"website\" />\n ${urlMeta ?? ''}\n ${descriptionMeta ?? ''}\n ${imageMeta ?? ''}\n `;\n}\n\nexport function splashJs(): string {\n return `\n <script>\n setTimeout(function() {\n if (!document.querySelector('#app.loading')) {\n return;\n }\n\n document.getElementById('splash').style.opacity = 1;\n }, 1000);\n </script>\n `;\n}\n\nexport function svg(this: HTMLEvalScope, path: string): string {\n return this.readFile(path);\n}\n","import Vue from '@vitejs/plugin-vue';\nimport { objectWithoutEmpty } from '@noeldemartin/utils';\nimport { resolve } from 'path';\nimport { VitePWA } from 'vite-plugin-pwa';\nimport type { ComponentResolver } from 'unplugin-vue-components';\nimport type { Plugin } from 'vite';\n\nimport { generate404Assets } from '@/lib/404';\nimport { generateSolidAssets, generateSolidVirtualModule, solidMiddleware } from '@/lib/solid';\nimport { guessMediaType } from '@/lib/media-types';\nimport { loadPackageInfo } from '@/lib/package-parser';\nimport { renderHTML } from '@/lib/html';\nimport type { AppInfo, Options } from '@/lib/options';\n\nimport type { VirtualAerogel } from 'virtual:aerogel';\n\nexport function AerogelResolver(): ComponentResolver {\n return {\n type: 'component',\n resolve: (name) => {\n if (!name.startsWith('AG') || name.startsWith('AGStory')) {\n return;\n }\n\n if (name.startsWith('AGSolid')) {\n return { name, as: name, from: '@aerogel/plugin-solid' };\n }\n\n if (name.startsWith('AGCloud')) {\n return { name, as: name, from: '@aerogel/plugin-offline-first' };\n }\n\n return { name, as: name, from: '@aerogel/core' };\n },\n };\n}\n\nexport default function Aerogel(options: Options = {}): Plugin[] {\n const app: AppInfo = {\n name: options.name ?? 'App',\n description: options.description,\n basePath: '/',\n baseUrl: options.baseUrl,\n themeColor: options.themeColor ?? '#ffffff',\n };\n const virtualHandlers: Record<string, () => string> = {\n 'virtual:aerogel'() {\n const virtual: VirtualAerogel = {\n environment: process.env.NODE_ENV ?? 'development',\n basePath: app.basePath,\n sourceUrl: app.sourceUrl,\n };\n\n return `export default ${JSON.stringify(virtual)};`;\n },\n 'virtual:aerogel-solid': () => generateSolidVirtualModule(app, options),\n };\n const AerogelPlugin: Plugin = {\n name: 'vite:aerogel',\n buildStart(buildOptions) {\n if (!Array.isArray(buildOptions.input) || !buildOptions.input[0]) {\n return;\n }\n\n loadPackageInfo(app, resolve(buildOptions.input[0], '../package.json'));\n },\n configureServer(server) {\n server.watcher.on('ready', () => {\n app.baseUrl = server.resolvedUrls?.network?.[0] ?? server.resolvedUrls?.local?.[0] ?? app.baseUrl;\n });\n\n server.middlewares.use(solidMiddleware(app, options));\n\n loadPackageInfo(app, `${server.config.root}/package.json`);\n },\n config: (config) => {\n app.basePath = config.base ?? app.basePath;\n config.optimizeDeps = config.optimizeDeps ?? {};\n config.optimizeDeps.exclude = [...(config.optimizeDeps.exclude ?? []), ...Object.keys(virtualHandlers)];\n\n return config;\n },\n generateBundle() {\n generate404Assets(this, app, options);\n generateSolidAssets(this, app, options);\n },\n load: (id) => virtualHandlers[id]?.(),\n resolveId: (id) => (id in virtualHandlers ? id : undefined),\n transformIndexHtml: (html, context) => renderHTML(html, context.filename, app),\n };\n\n return [\n Vue(),\n VitePWA({\n registerType: 'autoUpdate',\n manifest: objectWithoutEmpty({\n name: app.name,\n short_name: app.name,\n description: app.description,\n theme_color: options.themeColor,\n icons:\n options.icons &&\n Object.entries(options.icons).map(([sizes, src]) =>\n objectWithoutEmpty({\n src,\n sizes,\n type: guessMediaType(src),\n })),\n }),\n }),\n AerogelPlugin,\n ].flat();\n}\n","import { render } from 'mustache';\nimport type { PluginContext } from 'rollup';\n\nimport type { AppInfo, Options } from '@/lib/options';\n\nimport static404RedirectHTML from '../templates/404.html';\n\nexport function generate404Assets(context: PluginContext, app: AppInfo, options: Options): void {\n const static404Redirect = options.static404Redirect ?? process.env.NODE_ENV === 'production';\n\n if (!static404Redirect) {\n return;\n }\n\n context.emitFile({\n type: 'asset',\n fileName: typeof options.static404Redirect === 'string' ? options.static404Redirect : '404.html',\n source: render(static404RedirectHTML, { app }),\n });\n}\n","import { dirname, resolve } from 'path';\nimport { readFileSync } from 'fs';\nimport { stringMatchAll, toString } from '@noeldemartin/utils';\n\nimport type { AppInfo } from '@/lib/options';\n\nimport * as globalScope from './html-eval';\nimport type { HTMLEvalScope } from './html-eval';\n\nfunction evalScript(script: string, scope: HTMLEvalScope): unknown {\n return Function(`with (this) { return ${script} }`).bind({\n ...globalScope,\n ...scope,\n })();\n}\n\nexport function renderHTML(html: string, filename: string, app: AppInfo): string {\n const matches = stringMatchAll<2>(html, /{{(.*?)}}/g);\n const directory = dirname(filename);\n const readFile = (path: string) => readFileSync(resolve(directory, path)).toString();\n\n for (const match of matches) {\n html = html.replace(\n match[0],\n toString(\n evalScript(match[1], {\n app,\n readFile,\n }),\n ),\n );\n }\n\n return html;\n}\n"],"names":["createClientIDDocument","app","options","baseUrl","console","warn","endsWith","logoPublicPath","icons","Object","values","pop","clientID","client_id","client_name","name","redirect_uris","client_uri","scope","grant_types","response_types","logo_uri","mediaTypeByExtension","apng","avif","bmp","cur","gif","ico","jfif","jpeg","jpg","pjp","pjpeg","png","svg","tif","tiff","webp","guessMediaType","filename","extension","match","getSourceUrl","packageJson","repository","url","replace","type","getPlugins","keys","dependencies","filter","dependency","startsWith","map","plugin","substring","loadPackageInfo","packageJsonPath","existsSync","JSON","parse","readFileSync","toString","parsed","objectWithoutEmpty","sourceUrl","plugins","assign","path","this","readFile","arguments","length","undefined","maskIconColor","urlMeta","descriptionMeta","description","imageMeta","image","themeColor","resolve","as","from","basePath","virtualHandlers","virtual","environment","process","env","NODE_ENV","stringify","virtual:aerogel-solid","generateSolidVirtualModule","AerogelPlugin","buildStart","buildOptions","Array","isArray","input","configureServer","server","watcher","on","resolvedUrls","network","local","middlewares","use","request","response","next","statusCode","setHeader","write","end","solidMiddleware","config","root","base","optimizeDeps","exclude","generateBundle","context","static404Redirect","emitFile","fileName","source","render","static404RedirectHTML","generate404Assets","includes","generateSolidAssets","load","id","resolveId","transformIndexHtml","html","matches","stringMatchAll","directory","dirname","script","Function","bind","globalScope","renderHTML","Vue","VitePWA","registerType","manifest","short_name","theme_color","entries","_ref","sizes","src","flat"],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ComponentResolver } from 'unplugin-vue-components';
|
|
2
|
+
import type { Plugin as Plugin_2 } from 'vite';
|
|
3
|
+
|
|
4
|
+
declare function Aerogel(options?: Options): Plugin_2[];
|
|
5
|
+
export default Aerogel;
|
|
6
|
+
|
|
7
|
+
export declare function AerogelResolver(): ComponentResolver;
|
|
8
|
+
|
|
9
|
+
declare interface Options {
|
|
10
|
+
name?: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
themeColor?: string;
|
|
13
|
+
icons?: Record<string, string>;
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
static404Redirect?: boolean | string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { }
|
|
19
|
+
|
|
20
|
+
declare module 'virtual:aerogel-solid' {
|
|
21
|
+
export interface VirtualAerogelSolid {
|
|
22
|
+
clientID?: ClientIDDocument;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ClientIDDocument {
|
|
26
|
+
'@context': 'https://www.w3.org/ns/solid/oidc-context.jsonld';
|
|
27
|
+
client_id: string;
|
|
28
|
+
client_name: string;
|
|
29
|
+
redirect_uris: string[];
|
|
30
|
+
client_uri: string;
|
|
31
|
+
logo_uri?: string;
|
|
32
|
+
scope: string;
|
|
33
|
+
grant_types: string[];
|
|
34
|
+
response_types: string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const virtual: VirtualAerogelSolid;
|
|
38
|
+
|
|
39
|
+
export default virtual;
|
|
40
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"core-js/modules/esnext.async-iterator.map.js";import"core-js/modules/esnext.iterator.map.js";import e from"@vitejs/plugin-vue";import{objectWithoutEmpty as t,stringMatchAll as n,toString as i}from"@noeldemartin/utils";import{dirname as r,resolve as o}from"path";import{VitePWA as s}from"vite-plugin-pwa";import{render as a}from"mustache";import"core-js/modules/esnext.async-iterator.filter.js";import"core-js/modules/esnext.iterator.constructor.js";import"core-js/modules/esnext.iterator.filter.js";import{existsSync as l,readFileSync as p}from"fs";var c='<!DOCTYPE html>\n<html lang="en">\n <head>\n <meta charset="UTF-8" />\n <link rel="icon" href="/favicon.ico" />\n <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n <title>{{ app.name }}</title>\n <script>\n (function () {\n const basePath = \'{{{ app.basePath }}}\';\n const url = new URL(location.href);\n\n if (!url.pathname.startsWith(basePath)) {\n const link = document.createElement(\'link\');\n\n link.rel = \'stylesheet\';\n link.href = \'https://cdn.simplecss.org/simple.min.css\';\n\n document.head.appendChild(link);\n\n return;\n }\n\n const queryParameters = {};\n\n url.searchParams.forEach((value, key) => (queryParameters[key] = value));\n\n localStorage.setItem(\n \'static-404-redirect\',\n JSON.stringify({\n path: url.pathname.substring(basePath.length - 1),\n hash: url.hash,\n query: queryParameters,\n })\n );\n\n location.href = basePath;\n })();\n <\/script>\n\n <style>\n body {\n width: 100vw;\n height: 100vh;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n }\n </style>\n </head>\n <body>\n <h1>404 | Page not found</h1>\n <a href="/">go home</a>\n </body>\n</html>\n';function m(e,t){if(!e.baseUrl)return console.warn("It was not possible to create a Solid ClientID document because the baseUrl was not provided, to remove this warning add a `baseUrl` option in the Aerogel vite plugin options."),null;const n=e.baseUrl.endsWith("/")?e.baseUrl:`${e.baseUrl}/`,i=t.icons&&(t.icons["512x512"]??Object.values(t.icons).pop()),r={"@context":"https://www.w3.org/ns/solid/oidc-context.jsonld",client_id:`${n}clientid.jsonld`,client_name:e.name,redirect_uris:[n],client_uri:n,scope:"openid profile offline_access webid",grant_types:["refresh_token","authorization_code"],response_types:["code"]};return i&&(r.logo_uri=n+i),r}const u={apng:"image/apng",avif:"image/avif",bmp:"image/bmp",cur:"image/x-icon",gif:"image/gif",ico:"image/x-icon",jfif:"image/jpeg",jpeg:"image/jpeg",jpg:"image/jpeg",pjp:"image/jpeg",pjpeg:"image/jpeg",png:"image/png",svg:"image/svg",tif:"image/tiff",tiff:"image/tiff",webp:"image/webp"};function d(e){const t=e.match(/\.([^.]+)$/)?.[1];return(t&&u[t])??null}function g(e){return e.repository?"object"==typeof e.repository?e.repository.url.replace(`.${e.repository.type}`,""):e.repository?.replace("github:","https://github.com/"):null}function f(e){return Object.keys(e.dependencies??{}).filter((e=>e.startsWith("@aerogel/plugin-"))).map((e=>e.substring(16)))}function h(e,n){if(!l(n))return;const i=JSON.parse(p(n).toString()),r=t({sourceUrl:g(i),plugins:f(i)});Object.assign(e,r)}var y=Object.freeze({__proto__:null,css:function(e){return`<style>${this.readFile(e)}</style>`},favicons:function(){return`\n <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />\n <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />\n <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />\n <link rel="mask-icon" href="/safari-pinned-tab.svg" color="${(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}).maskIconColor??"#000000"}" />\n `},js:function(e){return`<script>${this.readFile(e)}<\/script>`},socialMeta:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const t=this.app.baseUrl&&`<meta property="og:url" content="${this.app.baseUrl}" />`,n=this.app.description&&`\n <meta property="og:description" content="${this.app.description}" />\n <meta name="description" content="${this.app.description}" />\n `,i=e.image&&`\n <meta property="og:image" content="${e.image}" />\n\n \x3c!-- TODO calculate real image dimensions (these are hard-coded) --\x3e\n <meta property="og:image:width" content="600" />\n <meta property="og:image:height" content="250" />\n `;return`\n <meta name="apple-mobile-web-app-title" content="${this.app.name}" />\n <meta name="application-name" content="${this.app.name}" />\n <meta name="msapplication-TileColor" content="${this.app.themeColor}" />\n <meta name="theme-color" content="${this.app.themeColor}" />\n <meta property="og:title" content="${this.app.name}" />\n <meta property="og:type" content="website" />\n ${t??""}\n ${n??""}\n ${i??""}\n `},splashJs:function(){return"\n <script>\n setTimeout(function() {\n if (!document.querySelector('#app.loading')) {\n return;\n }\n\n document.getElementById('splash').style.opacity = 1;\n }, 1000);\n <\/script>\n "},svg:function(e){return this.readFile(e)}});function b(){return{type:"component",resolve:e=>{if(e.startsWith("AG")&&!e.startsWith("AGStory"))return e.startsWith("AGSolid")?{name:e,as:e,from:"@aerogel/plugin-solid"}:e.startsWith("AGCloud")?{name:e,as:e,from:"@aerogel/plugin-offline-first"}:{name:e,as:e,from:"@aerogel/core"}}}}function v(){let l=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const u={name:l.name??"App",description:l.description,basePath:"/",baseUrl:l.baseUrl,themeColor:l.themeColor??"#ffffff"},g={"virtual:aerogel"(){const e={environment:process.env.NODE_ENV??"development",basePath:u.basePath,sourceUrl:u.sourceUrl};return`export default ${JSON.stringify(e)};`},"virtual:aerogel-solid":()=>function(e,n){const i=t({clientID:m(e,n)});return`export default ${JSON.stringify(i)};`}(u,l)},f={name:"vite:aerogel",buildStart(e){Array.isArray(e.input)&&e.input[0]&&h(u,o(e.input[0],"../package.json"))},configureServer(e){e.watcher.on("ready",(()=>{u.baseUrl=e.resolvedUrls?.network?.[0]??e.resolvedUrls?.local?.[0]??u.baseUrl})),e.middlewares.use(function(e,t){return(n,i,r)=>{if(!n.url?.endsWith("/clientid.jsonld"))return void r();const o=m(e,t);o?(i.statusCode=200,i.setHeader("Content-Type","application/ld+json"),i.write(JSON.stringify(o),"utf-8"),i.end()):r()}}(u,l)),h(u,`${e.config.root}/package.json`)},config:e=>(u.basePath=e.base??u.basePath,e.optimizeDeps=e.optimizeDeps??{},e.optimizeDeps.exclude=[...e.optimizeDeps.exclude??[],...Object.keys(g)],e),generateBundle(){!function(e,t,n){(n.static404Redirect??"production"===process.env.NODE_ENV)&&e.emitFile({type:"asset",fileName:"string"==typeof n.static404Redirect?n.static404Redirect:"404.html",source:a(c,{app:t})})}(this,u,l),function(e,t,n){if(!t.plugins?.includes("solid"))return;const i=m(t,n);i&&e.emitFile({type:"asset",fileName:"clientid.jsonld",source:JSON.stringify(i)})}(this,u,l)},load:e=>g[e]?.(),resolveId:e=>e in g?e:void 0,transformIndexHtml:(e,t)=>function(e,t,s){const a=n(e,/{{(.*?)}}/g),l=r(t),c=e=>p(o(l,e)).toString();for(const t of a)e=e.replace(t[0],i((m=t[1],u={app:s,readFile:c},Function(`with (this) { return ${m} }`).bind({...y,...u})())));var m,u;return e}(e,t.filename,u)};return[e(),s({registerType:"autoUpdate",manifest:t({name:u.name,short_name:u.name,description:u.description,theme_color:l.themeColor,icons:l.icons&&Object.entries(l.icons).map((e=>{let[n,i]=e;return t({src:i,sizes:n,type:d(i)})}))})}),f].flat()}export{b as AerogelResolver,v as default};
|
|
2
|
+
//# sourceMappingURL=aerogel-vite.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aerogel-vite.esm.js","sources":["../src/lib/solid.ts","../src/lib/media-types.ts","../src/lib/package-parser.ts","../src/lib/html-eval.ts","../src/main.ts","../src/lib/404.ts","../src/lib/html.ts"],"sourcesContent":["import { objectWithoutEmpty } from '@noeldemartin/utils';\nimport type { Connect } from 'vite';\nimport type { PluginContext } from 'rollup';\n\nimport type { AppInfo, Options } from '@/lib/options';\n\nimport type { ClientIDDocument, VirtualAerogelSolid } from 'virtual:aerogel-solid';\n\nfunction createClientIDDocument(app: AppInfo, options: Options): ClientIDDocument | null {\n if (!app.baseUrl) {\n // eslint-disable-next-line no-console\n console.warn(\n 'It was not possible to create a Solid ClientID document because the baseUrl ' +\n 'was not provided, to remove this warning add a `baseUrl` option in the Aerogel vite plugin options.',\n );\n\n return null;\n }\n\n const baseUrl = app.baseUrl.endsWith('/') ? app.baseUrl : `${app.baseUrl}/`;\n const logoPublicPath = options.icons && (options.icons['512x512'] ?? Object.values(options.icons).pop());\n const clientID: ClientIDDocument = {\n '@context': 'https://www.w3.org/ns/solid/oidc-context.jsonld',\n 'client_id': `${baseUrl}clientid.jsonld`,\n 'client_name': app.name,\n 'redirect_uris': [baseUrl],\n 'client_uri': baseUrl,\n 'scope': 'openid profile offline_access webid',\n 'grant_types': ['refresh_token', 'authorization_code'],\n 'response_types': ['code'],\n };\n\n if (logoPublicPath) {\n clientID.logo_uri = baseUrl + logoPublicPath;\n }\n\n return clientID;\n}\n\nexport function generateSolidAssets(context: PluginContext, app: AppInfo, options: Options): void {\n if (!app.plugins?.includes('solid')) {\n return;\n }\n\n const clientID = createClientIDDocument(app, options);\n\n if (!clientID) {\n return;\n }\n\n context.emitFile({\n type: 'asset',\n fileName: 'clientid.jsonld',\n source: JSON.stringify(clientID),\n });\n}\n\nexport function generateSolidVirtualModule(app: AppInfo, options: Options): string {\n const virtual: VirtualAerogelSolid = objectWithoutEmpty({\n clientID: createClientIDDocument(app, options),\n });\n\n return `export default ${JSON.stringify(virtual)};`;\n}\n\nexport function solidMiddleware(app: AppInfo, options: Options): Connect.NextHandleFunction {\n return (request, response, next) => {\n if (!request.url?.endsWith('/clientid.jsonld')) {\n next();\n\n return;\n }\n\n const clientID = createClientIDDocument(app, options);\n\n if (!clientID) {\n next();\n\n return;\n }\n\n response.statusCode = 200;\n response.setHeader('Content-Type', 'application/ld+json');\n response.write(JSON.stringify(clientID), 'utf-8');\n response.end();\n };\n}\n","// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types#common_image_file_types\nconst mediaTypeByExtension: Record<string, string> = {\n apng: 'image/apng',\n avif: 'image/avif',\n bmp: 'image/bmp',\n cur: 'image/x-icon',\n gif: 'image/gif',\n ico: 'image/x-icon',\n jfif: 'image/jpeg',\n jpeg: 'image/jpeg',\n jpg: 'image/jpeg',\n pjp: 'image/jpeg',\n pjpeg: 'image/jpeg',\n png: 'image/png',\n svg: 'image/svg',\n tif: 'image/tiff',\n tiff: 'image/tiff',\n webp: 'image/webp',\n};\n\nexport function guessMediaType(filename: string): string | null {\n const extension = filename.match(/\\.([^.]+)$/)?.[1];\n\n return (extension && mediaTypeByExtension[extension]) ?? null;\n}\n","import { existsSync, readFileSync } from 'fs';\nimport { objectWithoutEmpty } from '@noeldemartin/utils';\n\nimport type { AppInfo } from '@/lib/options';\n\ninterface PackageJson {\n repository?: string | { type: string; url: string };\n dependencies?: Record<string, string>;\n}\n\nfunction getSourceUrl(packageJson: PackageJson): string | null {\n if (!packageJson.repository) {\n return null;\n }\n\n if (typeof packageJson.repository === 'object') {\n return packageJson.repository.url.replace(`.${packageJson.repository.type}`, '');\n }\n\n return packageJson.repository?.replace('github:', 'https://github.com/');\n}\n\nfunction getPlugins(packageJson: PackageJson): string[] {\n return Object.keys(packageJson.dependencies ?? {})\n .filter((dependency) => dependency.startsWith('@aerogel/plugin-'))\n .map((plugin) => plugin.substring(16));\n}\n\nexport function loadPackageInfo(app: AppInfo, packageJsonPath: string): void {\n if (!existsSync(packageJsonPath)) {\n return;\n }\n\n const packageJson = JSON.parse(readFileSync(packageJsonPath).toString()) as PackageJson;\n const parsed: Partial<AppInfo> = objectWithoutEmpty({\n sourceUrl: getSourceUrl(packageJson),\n plugins: getPlugins(packageJson),\n });\n\n Object.assign(app, parsed);\n}\n","import type { AppInfo } from '@/lib/options';\n\nexport interface HTMLEvalScope {\n app: AppInfo;\n readFile(path: string): string;\n}\n\nexport function css(this: HTMLEvalScope, path: string): string {\n return `<style>${this.readFile(path)}</style>`;\n}\n\nexport function favicons(options: { maskIconColor?: string } = {}): string {\n return `\n <link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/apple-touch-icon.png\" />\n <link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicon-32x32.png\" />\n <link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/favicon-16x16.png\" />\n <link rel=\"mask-icon\" href=\"/safari-pinned-tab.svg\" color=\"${options.maskIconColor ?? '#000000'}\" />\n `;\n}\n\nexport function js(this: HTMLEvalScope, path: string): string {\n return `<script>${this.readFile(path)}</script>`;\n}\n\nexport function socialMeta(this: HTMLEvalScope, options: { image?: string } = {}): string {\n const urlMeta = this.app.baseUrl && `<meta property=\"og:url\" content=\"${this.app.baseUrl}\" />`;\n const descriptionMeta =\n this.app.description &&\n `\n <meta property=\"og:description\" content=\"${this.app.description}\" />\n <meta name=\"description\" content=\"${this.app.description}\" />\n `;\n const imageMeta =\n options.image &&\n `\n <meta property=\"og:image\" content=\"${options.image}\" />\n\n <!-- TODO calculate real image dimensions (these are hard-coded) -->\n <meta property=\"og:image:width\" content=\"600\" />\n <meta property=\"og:image:height\" content=\"250\" />\n `;\n\n return `\n <meta name=\"apple-mobile-web-app-title\" content=\"${this.app.name}\" />\n <meta name=\"application-name\" content=\"${this.app.name}\" />\n <meta name=\"msapplication-TileColor\" content=\"${this.app.themeColor}\" />\n <meta name=\"theme-color\" content=\"${this.app.themeColor}\" />\n <meta property=\"og:title\" content=\"${this.app.name}\" />\n <meta property=\"og:type\" content=\"website\" />\n ${urlMeta ?? ''}\n ${descriptionMeta ?? ''}\n ${imageMeta ?? ''}\n `;\n}\n\nexport function splashJs(): string {\n return `\n <script>\n setTimeout(function() {\n if (!document.querySelector('#app.loading')) {\n return;\n }\n\n document.getElementById('splash').style.opacity = 1;\n }, 1000);\n </script>\n `;\n}\n\nexport function svg(this: HTMLEvalScope, path: string): string {\n return this.readFile(path);\n}\n","import Vue from '@vitejs/plugin-vue';\nimport { objectWithoutEmpty } from '@noeldemartin/utils';\nimport { resolve } from 'path';\nimport { VitePWA } from 'vite-plugin-pwa';\nimport type { ComponentResolver } from 'unplugin-vue-components';\nimport type { Plugin } from 'vite';\n\nimport { generate404Assets } from '@/lib/404';\nimport { generateSolidAssets, generateSolidVirtualModule, solidMiddleware } from '@/lib/solid';\nimport { guessMediaType } from '@/lib/media-types';\nimport { loadPackageInfo } from '@/lib/package-parser';\nimport { renderHTML } from '@/lib/html';\nimport type { AppInfo, Options } from '@/lib/options';\n\nimport type { VirtualAerogel } from 'virtual:aerogel';\n\nexport function AerogelResolver(): ComponentResolver {\n return {\n type: 'component',\n resolve: (name) => {\n if (!name.startsWith('AG') || name.startsWith('AGStory')) {\n return;\n }\n\n if (name.startsWith('AGSolid')) {\n return { name, as: name, from: '@aerogel/plugin-solid' };\n }\n\n if (name.startsWith('AGCloud')) {\n return { name, as: name, from: '@aerogel/plugin-offline-first' };\n }\n\n return { name, as: name, from: '@aerogel/core' };\n },\n };\n}\n\nexport default function Aerogel(options: Options = {}): Plugin[] {\n const app: AppInfo = {\n name: options.name ?? 'App',\n description: options.description,\n basePath: '/',\n baseUrl: options.baseUrl,\n themeColor: options.themeColor ?? '#ffffff',\n };\n const virtualHandlers: Record<string, () => string> = {\n 'virtual:aerogel'() {\n const virtual: VirtualAerogel = {\n environment: process.env.NODE_ENV ?? 'development',\n basePath: app.basePath,\n sourceUrl: app.sourceUrl,\n };\n\n return `export default ${JSON.stringify(virtual)};`;\n },\n 'virtual:aerogel-solid': () => generateSolidVirtualModule(app, options),\n };\n const AerogelPlugin: Plugin = {\n name: 'vite:aerogel',\n buildStart(buildOptions) {\n if (!Array.isArray(buildOptions.input) || !buildOptions.input[0]) {\n return;\n }\n\n loadPackageInfo(app, resolve(buildOptions.input[0], '../package.json'));\n },\n configureServer(server) {\n server.watcher.on('ready', () => {\n app.baseUrl = server.resolvedUrls?.network?.[0] ?? server.resolvedUrls?.local?.[0] ?? app.baseUrl;\n });\n\n server.middlewares.use(solidMiddleware(app, options));\n\n loadPackageInfo(app, `${server.config.root}/package.json`);\n },\n config: (config) => {\n app.basePath = config.base ?? app.basePath;\n config.optimizeDeps = config.optimizeDeps ?? {};\n config.optimizeDeps.exclude = [...(config.optimizeDeps.exclude ?? []), ...Object.keys(virtualHandlers)];\n\n return config;\n },\n generateBundle() {\n generate404Assets(this, app, options);\n generateSolidAssets(this, app, options);\n },\n load: (id) => virtualHandlers[id]?.(),\n resolveId: (id) => (id in virtualHandlers ? id : undefined),\n transformIndexHtml: (html, context) => renderHTML(html, context.filename, app),\n };\n\n return [\n Vue(),\n VitePWA({\n registerType: 'autoUpdate',\n manifest: objectWithoutEmpty({\n name: app.name,\n short_name: app.name,\n description: app.description,\n theme_color: options.themeColor,\n icons:\n options.icons &&\n Object.entries(options.icons).map(([sizes, src]) =>\n objectWithoutEmpty({\n src,\n sizes,\n type: guessMediaType(src),\n })),\n }),\n }),\n AerogelPlugin,\n ].flat();\n}\n","import { render } from 'mustache';\nimport type { PluginContext } from 'rollup';\n\nimport type { AppInfo, Options } from '@/lib/options';\n\nimport static404RedirectHTML from '../templates/404.html';\n\nexport function generate404Assets(context: PluginContext, app: AppInfo, options: Options): void {\n const static404Redirect = options.static404Redirect ?? process.env.NODE_ENV === 'production';\n\n if (!static404Redirect) {\n return;\n }\n\n context.emitFile({\n type: 'asset',\n fileName: typeof options.static404Redirect === 'string' ? options.static404Redirect : '404.html',\n source: render(static404RedirectHTML, { app }),\n });\n}\n","import { dirname, resolve } from 'path';\nimport { readFileSync } from 'fs';\nimport { stringMatchAll, toString } from '@noeldemartin/utils';\n\nimport type { AppInfo } from '@/lib/options';\n\nimport * as globalScope from './html-eval';\nimport type { HTMLEvalScope } from './html-eval';\n\nfunction evalScript(script: string, scope: HTMLEvalScope): unknown {\n return Function(`with (this) { return ${script} }`).bind({\n ...globalScope,\n ...scope,\n })();\n}\n\nexport function renderHTML(html: string, filename: string, app: AppInfo): string {\n const matches = stringMatchAll<2>(html, /{{(.*?)}}/g);\n const directory = dirname(filename);\n const readFile = (path: string) => readFileSync(resolve(directory, path)).toString();\n\n for (const match of matches) {\n html = html.replace(\n match[0],\n toString(\n evalScript(match[1], {\n app,\n readFile,\n }),\n ),\n );\n }\n\n return html;\n}\n"],"names":["createClientIDDocument","app","options","baseUrl","console","warn","endsWith","logoPublicPath","icons","Object","values","pop","clientID","client_id","client_name","name","redirect_uris","client_uri","scope","grant_types","response_types","logo_uri","mediaTypeByExtension","apng","avif","bmp","cur","gif","ico","jfif","jpeg","jpg","pjp","pjpeg","png","svg","tif","tiff","webp","guessMediaType","filename","extension","match","getSourceUrl","packageJson","repository","url","replace","type","getPlugins","keys","dependencies","filter","dependency","startsWith","map","plugin","substring","loadPackageInfo","packageJsonPath","existsSync","JSON","parse","readFileSync","toString","parsed","objectWithoutEmpty","sourceUrl","plugins","assign","path","this","readFile","arguments","length","undefined","maskIconColor","urlMeta","descriptionMeta","description","imageMeta","image","themeColor","AerogelResolver","resolve","as","from","Aerogel","basePath","virtualHandlers","virtual","environment","process","env","NODE_ENV","stringify","virtual:aerogel-solid","generateSolidVirtualModule","AerogelPlugin","buildStart","buildOptions","Array","isArray","input","configureServer","server","watcher","on","resolvedUrls","network","local","middlewares","use","request","response","next","statusCode","setHeader","write","end","solidMiddleware","config","root","base","optimizeDeps","exclude","generateBundle","context","static404Redirect","emitFile","fileName","source","render","static404RedirectHTML","generate404Assets","includes","generateSolidAssets","load","id","resolveId","transformIndexHtml","html","matches","stringMatchAll","directory","dirname","script","Function","bind","globalScope","renderHTML","Vue","VitePWA","registerType","manifest","short_name","theme_color","entries","_ref","sizes","src","flat"],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
declare module 'virtual:aerogel' {
|
|
2
|
+
export interface VirtualAerogel {
|
|
3
|
+
environment: 'production' | 'development' | 'testing' | 'test' | string;
|
|
4
|
+
basePath?: string;
|
|
5
|
+
sourceUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const virtual: VirtualAerogel;
|
|
9
|
+
|
|
10
|
+
export default virtual;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare module 'virtual:aerogel-solid' {
|
|
14
|
+
export interface VirtualAerogelSolid {
|
|
15
|
+
clientID?: ClientIDDocument;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ClientIDDocument {
|
|
19
|
+
'@context': 'https://www.w3.org/ns/solid/oidc-context.jsonld';
|
|
20
|
+
client_id: string;
|
|
21
|
+
client_name: string;
|
|
22
|
+
redirect_uris: string[];
|
|
23
|
+
client_uri: string;
|
|
24
|
+
logo_uri?: string;
|
|
25
|
+
scope: string;
|
|
26
|
+
grant_types: string[];
|
|
27
|
+
response_types: string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const virtual: VirtualAerogelSolid;
|
|
31
|
+
|
|
32
|
+
export default virtual;
|
|
33
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aerogel/vite",
|
|
3
|
+
"description": "Vite Plugin to build Aerogel apps",
|
|
4
|
+
"version": "0.0.0-next.2fb667e321e0740b4db1a21bc948232a9bf219d6",
|
|
5
|
+
"main": "dist/aerogel-vite.cjs.js",
|
|
6
|
+
"module": "dist/aerogel-vite.esm.js",
|
|
7
|
+
"types": "dist/aerogel-vite.d.ts",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "rm dist -rf && npm run build:js && npm run build:types",
|
|
11
|
+
"build:js": "noeldemartin-build-javascript",
|
|
12
|
+
"build:types": "noeldemartin-build-types && cp src/types/virtual.d.ts dist",
|
|
13
|
+
"lint": "noeldemartin-lint src",
|
|
14
|
+
"publish-next": "noeldemartin-publish-next"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18.x"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/NoelDeMartin/aerogeljs.git"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"aerogel",
|
|
25
|
+
"vite"
|
|
26
|
+
],
|
|
27
|
+
"author": "Noel De Martin",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/NoelDeMartin/aerogeljs/issues"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@aerogel/core": "*",
|
|
34
|
+
"vite": "*"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@noeldemartin/utils": "0.5.1-next.49cc6c9b4a20930cbf922a949135981791acc5c3",
|
|
38
|
+
"@vitejs/plugin-vue": "^4.1.0",
|
|
39
|
+
"mustache": "^4.2.0",
|
|
40
|
+
"rollup": "^3.29.4",
|
|
41
|
+
"unplugin-vue-components": "^0.25.2",
|
|
42
|
+
"vite-plugin-pwa": "^0.17.2"
|
|
43
|
+
},
|
|
44
|
+
"eslintConfig": {
|
|
45
|
+
"extends": [
|
|
46
|
+
"@noeldemartin/eslint-config-typescript"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/lib/404.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { render } from 'mustache';
|
|
2
|
+
import type { PluginContext } from 'rollup';
|
|
3
|
+
|
|
4
|
+
import type { AppInfo, Options } from '@/lib/options';
|
|
5
|
+
|
|
6
|
+
import static404RedirectHTML from '../templates/404.html';
|
|
7
|
+
|
|
8
|
+
export function generate404Assets(context: PluginContext, app: AppInfo, options: Options): void {
|
|
9
|
+
const static404Redirect = options.static404Redirect ?? process.env.NODE_ENV === 'production';
|
|
10
|
+
|
|
11
|
+
if (!static404Redirect) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
context.emitFile({
|
|
16
|
+
type: 'asset',
|
|
17
|
+
fileName: typeof options.static404Redirect === 'string' ? options.static404Redirect : '404.html',
|
|
18
|
+
source: render(static404RedirectHTML, { app }),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { AppInfo } from '@/lib/options';
|
|
2
|
+
|
|
3
|
+
export interface HTMLEvalScope {
|
|
4
|
+
app: AppInfo;
|
|
5
|
+
readFile(path: string): string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function css(this: HTMLEvalScope, path: string): string {
|
|
9
|
+
return `<style>${this.readFile(path)}</style>`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function favicons(options: { maskIconColor?: string } = {}): string {
|
|
13
|
+
return `
|
|
14
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
|
15
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
|
16
|
+
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
|
17
|
+
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="${options.maskIconColor ?? '#000000'}" />
|
|
18
|
+
`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function js(this: HTMLEvalScope, path: string): string {
|
|
22
|
+
return `<script>${this.readFile(path)}</script>`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function socialMeta(this: HTMLEvalScope, options: { image?: string } = {}): string {
|
|
26
|
+
const urlMeta = this.app.baseUrl && `<meta property="og:url" content="${this.app.baseUrl}" />`;
|
|
27
|
+
const descriptionMeta =
|
|
28
|
+
this.app.description &&
|
|
29
|
+
`
|
|
30
|
+
<meta property="og:description" content="${this.app.description}" />
|
|
31
|
+
<meta name="description" content="${this.app.description}" />
|
|
32
|
+
`;
|
|
33
|
+
const imageMeta =
|
|
34
|
+
options.image &&
|
|
35
|
+
`
|
|
36
|
+
<meta property="og:image" content="${options.image}" />
|
|
37
|
+
|
|
38
|
+
<!-- TODO calculate real image dimensions (these are hard-coded) -->
|
|
39
|
+
<meta property="og:image:width" content="600" />
|
|
40
|
+
<meta property="og:image:height" content="250" />
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
return `
|
|
44
|
+
<meta name="apple-mobile-web-app-title" content="${this.app.name}" />
|
|
45
|
+
<meta name="application-name" content="${this.app.name}" />
|
|
46
|
+
<meta name="msapplication-TileColor" content="${this.app.themeColor}" />
|
|
47
|
+
<meta name="theme-color" content="${this.app.themeColor}" />
|
|
48
|
+
<meta property="og:title" content="${this.app.name}" />
|
|
49
|
+
<meta property="og:type" content="website" />
|
|
50
|
+
${urlMeta ?? ''}
|
|
51
|
+
${descriptionMeta ?? ''}
|
|
52
|
+
${imageMeta ?? ''}
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function splashJs(): string {
|
|
57
|
+
return `
|
|
58
|
+
<script>
|
|
59
|
+
setTimeout(function() {
|
|
60
|
+
if (!document.querySelector('#app.loading')) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
document.getElementById('splash').style.opacity = 1;
|
|
65
|
+
}, 1000);
|
|
66
|
+
</script>
|
|
67
|
+
`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function svg(this: HTMLEvalScope, path: string): string {
|
|
71
|
+
return this.readFile(path);
|
|
72
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { formatCodeBlock } from '@noeldemartin/utils';
|
|
3
|
+
|
|
4
|
+
import type { AppInfo } from '@/lib/options';
|
|
5
|
+
|
|
6
|
+
import { renderHTML } from './html';
|
|
7
|
+
|
|
8
|
+
describe('HTML helpers', () => {
|
|
9
|
+
|
|
10
|
+
const app: AppInfo = {
|
|
11
|
+
name: 'My App',
|
|
12
|
+
basePath: '/',
|
|
13
|
+
themeColor: '#654321',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
it('Evaluates app info', () => {
|
|
17
|
+
// Arrange
|
|
18
|
+
const html = '<title>{{ app.name }}</title>';
|
|
19
|
+
const expected = '<title>My App</title>';
|
|
20
|
+
|
|
21
|
+
// Act
|
|
22
|
+
const actual = renderHTML(html, '/var/www/index.html', app);
|
|
23
|
+
|
|
24
|
+
// Assert
|
|
25
|
+
expect(actual).toEqual(expected);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('Evaluates asset helpers', () => {
|
|
29
|
+
// Arrange
|
|
30
|
+
const html = `
|
|
31
|
+
{{ css('./inline-styles.css') }}
|
|
32
|
+
{{ js('./inline-script.js') }}
|
|
33
|
+
{{ svg('./inline-svg.svg') }}
|
|
34
|
+
`;
|
|
35
|
+
const expected = `
|
|
36
|
+
<style>file-source[/var/www/inline-styles.css]</style>
|
|
37
|
+
<script>file-source[/var/www/inline-script.js]</script>
|
|
38
|
+
file-source[/var/www/inline-svg.svg]
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
// Act
|
|
42
|
+
const actual = renderHTML(html, '/var/www/index.html', app);
|
|
43
|
+
|
|
44
|
+
// Assert
|
|
45
|
+
expect(actual).toEqual(expected);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('Evaluates meta helpers', () => {
|
|
49
|
+
// Arrange
|
|
50
|
+
const html = `
|
|
51
|
+
{{ favicons({ maskIconColor: '#123456' }) }}
|
|
52
|
+
{{ socialMeta() }}
|
|
53
|
+
`;
|
|
54
|
+
const expected = formatCodeBlock(`
|
|
55
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
|
56
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
|
57
|
+
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
|
58
|
+
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#123456" />
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
<meta name="apple-mobile-web-app-title" content="My App" />
|
|
62
|
+
<meta name="application-name" content="My App" />
|
|
63
|
+
<meta name="msapplication-TileColor" content="#654321" />
|
|
64
|
+
<meta name="theme-color" content="#654321" />
|
|
65
|
+
<meta property="og:title" content="My App" />
|
|
66
|
+
<meta property="og:type" content="website" />
|
|
67
|
+
`);
|
|
68
|
+
|
|
69
|
+
// Act
|
|
70
|
+
const actual = renderHTML(html, '/var/www/index.html', app);
|
|
71
|
+
|
|
72
|
+
// Assert
|
|
73
|
+
expect(formatCodeBlock(actual)).toEqual(expected);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
});
|
package/src/lib/html.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { dirname, resolve } from 'path';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { stringMatchAll, toString } from '@noeldemartin/utils';
|
|
4
|
+
|
|
5
|
+
import type { AppInfo } from '@/lib/options';
|
|
6
|
+
|
|
7
|
+
import * as globalScope from './html-eval';
|
|
8
|
+
import type { HTMLEvalScope } from './html-eval';
|
|
9
|
+
|
|
10
|
+
function evalScript(script: string, scope: HTMLEvalScope): unknown {
|
|
11
|
+
return Function(`with (this) { return ${script} }`).bind({
|
|
12
|
+
...globalScope,
|
|
13
|
+
...scope,
|
|
14
|
+
})();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function renderHTML(html: string, filename: string, app: AppInfo): string {
|
|
18
|
+
const matches = stringMatchAll<2>(html, /{{(.*?)}}/g);
|
|
19
|
+
const directory = dirname(filename);
|
|
20
|
+
const readFile = (path: string) => readFileSync(resolve(directory, path)).toString();
|
|
21
|
+
|
|
22
|
+
for (const match of matches) {
|
|
23
|
+
html = html.replace(
|
|
24
|
+
match[0],
|
|
25
|
+
toString(
|
|
26
|
+
evalScript(match[1], {
|
|
27
|
+
app,
|
|
28
|
+
readFile,
|
|
29
|
+
}),
|
|
30
|
+
),
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return html;
|
|
35
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { guessMediaType } from '@/lib/media-types';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
describe('Media Types helpers', () => {
|
|
5
|
+
|
|
6
|
+
it('guesses media types from filenames', () => {
|
|
7
|
+
expect(guessMediaType('foo.png')).toBe('image/png');
|
|
8
|
+
expect(guessMediaType('foo.bar.png')).toBe('image/png');
|
|
9
|
+
expect(guessMediaType('foo.jpg')).toBe('image/jpeg');
|
|
10
|
+
expect(guessMediaType('foo.jpeg')).toBe('image/jpeg');
|
|
11
|
+
expect(guessMediaType('foo')).toBeNull();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types#common_image_file_types
|
|
2
|
+
const mediaTypeByExtension: Record<string, string> = {
|
|
3
|
+
apng: 'image/apng',
|
|
4
|
+
avif: 'image/avif',
|
|
5
|
+
bmp: 'image/bmp',
|
|
6
|
+
cur: 'image/x-icon',
|
|
7
|
+
gif: 'image/gif',
|
|
8
|
+
ico: 'image/x-icon',
|
|
9
|
+
jfif: 'image/jpeg',
|
|
10
|
+
jpeg: 'image/jpeg',
|
|
11
|
+
jpg: 'image/jpeg',
|
|
12
|
+
pjp: 'image/jpeg',
|
|
13
|
+
pjpeg: 'image/jpeg',
|
|
14
|
+
png: 'image/png',
|
|
15
|
+
svg: 'image/svg',
|
|
16
|
+
tif: 'image/tiff',
|
|
17
|
+
tiff: 'image/tiff',
|
|
18
|
+
webp: 'image/webp',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function guessMediaType(filename: string): string | null {
|
|
22
|
+
const extension = filename.match(/\.([^.]+)$/)?.[1];
|
|
23
|
+
|
|
24
|
+
return (extension && mediaTypeByExtension[extension]) ?? null;
|
|
25
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface Options {
|
|
2
|
+
name?: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
themeColor?: string;
|
|
5
|
+
icons?: Record<string, string>;
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
static404Redirect?: boolean | string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface AppInfo {
|
|
11
|
+
name: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
basePath: string;
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
sourceUrl?: string;
|
|
16
|
+
themeColor: string;
|
|
17
|
+
plugins?: string[];
|
|
18
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { objectWithoutEmpty } from '@noeldemartin/utils';
|
|
3
|
+
|
|
4
|
+
import type { AppInfo } from '@/lib/options';
|
|
5
|
+
|
|
6
|
+
interface PackageJson {
|
|
7
|
+
repository?: string | { type: string; url: string };
|
|
8
|
+
dependencies?: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getSourceUrl(packageJson: PackageJson): string | null {
|
|
12
|
+
if (!packageJson.repository) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (typeof packageJson.repository === 'object') {
|
|
17
|
+
return packageJson.repository.url.replace(`.${packageJson.repository.type}`, '');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return packageJson.repository?.replace('github:', 'https://github.com/');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getPlugins(packageJson: PackageJson): string[] {
|
|
24
|
+
return Object.keys(packageJson.dependencies ?? {})
|
|
25
|
+
.filter((dependency) => dependency.startsWith('@aerogel/plugin-'))
|
|
26
|
+
.map((plugin) => plugin.substring(16));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function loadPackageInfo(app: AppInfo, packageJsonPath: string): void {
|
|
30
|
+
if (!existsSync(packageJsonPath)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath).toString()) as PackageJson;
|
|
35
|
+
const parsed: Partial<AppInfo> = objectWithoutEmpty({
|
|
36
|
+
sourceUrl: getSourceUrl(packageJson),
|
|
37
|
+
plugins: getPlugins(packageJson),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
Object.assign(app, parsed);
|
|
41
|
+
}
|
package/src/lib/solid.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { objectWithoutEmpty } from '@noeldemartin/utils';
|
|
2
|
+
import type { Connect } from 'vite';
|
|
3
|
+
import type { PluginContext } from 'rollup';
|
|
4
|
+
|
|
5
|
+
import type { AppInfo, Options } from '@/lib/options';
|
|
6
|
+
|
|
7
|
+
import type { ClientIDDocument, VirtualAerogelSolid } from 'virtual:aerogel-solid';
|
|
8
|
+
|
|
9
|
+
function createClientIDDocument(app: AppInfo, options: Options): ClientIDDocument | null {
|
|
10
|
+
if (!app.baseUrl) {
|
|
11
|
+
// eslint-disable-next-line no-console
|
|
12
|
+
console.warn(
|
|
13
|
+
'It was not possible to create a Solid ClientID document because the baseUrl ' +
|
|
14
|
+
'was not provided, to remove this warning add a `baseUrl` option in the Aerogel vite plugin options.',
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const baseUrl = app.baseUrl.endsWith('/') ? app.baseUrl : `${app.baseUrl}/`;
|
|
21
|
+
const logoPublicPath = options.icons && (options.icons['512x512'] ?? Object.values(options.icons).pop());
|
|
22
|
+
const clientID: ClientIDDocument = {
|
|
23
|
+
'@context': 'https://www.w3.org/ns/solid/oidc-context.jsonld',
|
|
24
|
+
'client_id': `${baseUrl}clientid.jsonld`,
|
|
25
|
+
'client_name': app.name,
|
|
26
|
+
'redirect_uris': [baseUrl],
|
|
27
|
+
'client_uri': baseUrl,
|
|
28
|
+
'scope': 'openid profile offline_access webid',
|
|
29
|
+
'grant_types': ['refresh_token', 'authorization_code'],
|
|
30
|
+
'response_types': ['code'],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
if (logoPublicPath) {
|
|
34
|
+
clientID.logo_uri = baseUrl + logoPublicPath;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return clientID;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function generateSolidAssets(context: PluginContext, app: AppInfo, options: Options): void {
|
|
41
|
+
if (!app.plugins?.includes('solid')) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const clientID = createClientIDDocument(app, options);
|
|
46
|
+
|
|
47
|
+
if (!clientID) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
context.emitFile({
|
|
52
|
+
type: 'asset',
|
|
53
|
+
fileName: 'clientid.jsonld',
|
|
54
|
+
source: JSON.stringify(clientID),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function generateSolidVirtualModule(app: AppInfo, options: Options): string {
|
|
59
|
+
const virtual: VirtualAerogelSolid = objectWithoutEmpty({
|
|
60
|
+
clientID: createClientIDDocument(app, options),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return `export default ${JSON.stringify(virtual)};`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function solidMiddleware(app: AppInfo, options: Options): Connect.NextHandleFunction {
|
|
67
|
+
return (request, response, next) => {
|
|
68
|
+
if (!request.url?.endsWith('/clientid.jsonld')) {
|
|
69
|
+
next();
|
|
70
|
+
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const clientID = createClientIDDocument(app, options);
|
|
75
|
+
|
|
76
|
+
if (!clientID) {
|
|
77
|
+
next();
|
|
78
|
+
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
response.statusCode = 200;
|
|
83
|
+
response.setHeader('Content-Type', 'application/ld+json');
|
|
84
|
+
response.write(JSON.stringify(clientID), 'utf-8');
|
|
85
|
+
response.end();
|
|
86
|
+
};
|
|
87
|
+
}
|
package/src/main.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import Vue from '@vitejs/plugin-vue';
|
|
2
|
+
import { objectWithoutEmpty } from '@noeldemartin/utils';
|
|
3
|
+
import { resolve } from 'path';
|
|
4
|
+
import { VitePWA } from 'vite-plugin-pwa';
|
|
5
|
+
import type { ComponentResolver } from 'unplugin-vue-components';
|
|
6
|
+
import type { Plugin } from 'vite';
|
|
7
|
+
|
|
8
|
+
import { generate404Assets } from '@/lib/404';
|
|
9
|
+
import { generateSolidAssets, generateSolidVirtualModule, solidMiddleware } from '@/lib/solid';
|
|
10
|
+
import { guessMediaType } from '@/lib/media-types';
|
|
11
|
+
import { loadPackageInfo } from '@/lib/package-parser';
|
|
12
|
+
import { renderHTML } from '@/lib/html';
|
|
13
|
+
import type { AppInfo, Options } from '@/lib/options';
|
|
14
|
+
|
|
15
|
+
import type { VirtualAerogel } from 'virtual:aerogel';
|
|
16
|
+
|
|
17
|
+
export function AerogelResolver(): ComponentResolver {
|
|
18
|
+
return {
|
|
19
|
+
type: 'component',
|
|
20
|
+
resolve: (name) => {
|
|
21
|
+
if (!name.startsWith('AG') || name.startsWith('AGStory')) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (name.startsWith('AGSolid')) {
|
|
26
|
+
return { name, as: name, from: '@aerogel/plugin-solid' };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (name.startsWith('AGCloud')) {
|
|
30
|
+
return { name, as: name, from: '@aerogel/plugin-offline-first' };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return { name, as: name, from: '@aerogel/core' };
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default function Aerogel(options: Options = {}): Plugin[] {
|
|
39
|
+
const app: AppInfo = {
|
|
40
|
+
name: options.name ?? 'App',
|
|
41
|
+
description: options.description,
|
|
42
|
+
basePath: '/',
|
|
43
|
+
baseUrl: options.baseUrl,
|
|
44
|
+
themeColor: options.themeColor ?? '#ffffff',
|
|
45
|
+
};
|
|
46
|
+
const virtualHandlers: Record<string, () => string> = {
|
|
47
|
+
'virtual:aerogel'() {
|
|
48
|
+
const virtual: VirtualAerogel = {
|
|
49
|
+
environment: process.env.NODE_ENV ?? 'development',
|
|
50
|
+
basePath: app.basePath,
|
|
51
|
+
sourceUrl: app.sourceUrl,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return `export default ${JSON.stringify(virtual)};`;
|
|
55
|
+
},
|
|
56
|
+
'virtual:aerogel-solid': () => generateSolidVirtualModule(app, options),
|
|
57
|
+
};
|
|
58
|
+
const AerogelPlugin: Plugin = {
|
|
59
|
+
name: 'vite:aerogel',
|
|
60
|
+
buildStart(buildOptions) {
|
|
61
|
+
if (!Array.isArray(buildOptions.input) || !buildOptions.input[0]) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
loadPackageInfo(app, resolve(buildOptions.input[0], '../package.json'));
|
|
66
|
+
},
|
|
67
|
+
configureServer(server) {
|
|
68
|
+
server.watcher.on('ready', () => {
|
|
69
|
+
app.baseUrl = server.resolvedUrls?.network?.[0] ?? server.resolvedUrls?.local?.[0] ?? app.baseUrl;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
server.middlewares.use(solidMiddleware(app, options));
|
|
73
|
+
|
|
74
|
+
loadPackageInfo(app, `${server.config.root}/package.json`);
|
|
75
|
+
},
|
|
76
|
+
config: (config) => {
|
|
77
|
+
app.basePath = config.base ?? app.basePath;
|
|
78
|
+
config.optimizeDeps = config.optimizeDeps ?? {};
|
|
79
|
+
config.optimizeDeps.exclude = [...(config.optimizeDeps.exclude ?? []), ...Object.keys(virtualHandlers)];
|
|
80
|
+
|
|
81
|
+
return config;
|
|
82
|
+
},
|
|
83
|
+
generateBundle() {
|
|
84
|
+
generate404Assets(this, app, options);
|
|
85
|
+
generateSolidAssets(this, app, options);
|
|
86
|
+
},
|
|
87
|
+
load: (id) => virtualHandlers[id]?.(),
|
|
88
|
+
resolveId: (id) => (id in virtualHandlers ? id : undefined),
|
|
89
|
+
transformIndexHtml: (html, context) => renderHTML(html, context.filename, app),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return [
|
|
93
|
+
Vue(),
|
|
94
|
+
VitePWA({
|
|
95
|
+
registerType: 'autoUpdate',
|
|
96
|
+
manifest: objectWithoutEmpty({
|
|
97
|
+
name: app.name,
|
|
98
|
+
short_name: app.name,
|
|
99
|
+
description: app.description,
|
|
100
|
+
theme_color: options.themeColor,
|
|
101
|
+
icons:
|
|
102
|
+
options.icons &&
|
|
103
|
+
Object.entries(options.icons).map(([sizes, src]) =>
|
|
104
|
+
objectWithoutEmpty({
|
|
105
|
+
src,
|
|
106
|
+
sizes,
|
|
107
|
+
type: guessMediaType(src),
|
|
108
|
+
})),
|
|
109
|
+
}),
|
|
110
|
+
}),
|
|
111
|
+
AerogelPlugin,
|
|
112
|
+
].flat();
|
|
113
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" href="/favicon.ico" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>{{ app.name }}</title>
|
|
8
|
+
<script>
|
|
9
|
+
(function () {
|
|
10
|
+
const basePath = '{{{ app.basePath }}}';
|
|
11
|
+
const url = new URL(location.href);
|
|
12
|
+
|
|
13
|
+
if (!url.pathname.startsWith(basePath)) {
|
|
14
|
+
const link = document.createElement('link');
|
|
15
|
+
|
|
16
|
+
link.rel = 'stylesheet';
|
|
17
|
+
link.href = 'https://cdn.simplecss.org/simple.min.css';
|
|
18
|
+
|
|
19
|
+
document.head.appendChild(link);
|
|
20
|
+
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const queryParameters = {};
|
|
25
|
+
|
|
26
|
+
url.searchParams.forEach((value, key) => (queryParameters[key] = value));
|
|
27
|
+
|
|
28
|
+
localStorage.setItem(
|
|
29
|
+
'static-404-redirect',
|
|
30
|
+
JSON.stringify({
|
|
31
|
+
path: url.pathname.substring(basePath.length - 1),
|
|
32
|
+
hash: url.hash,
|
|
33
|
+
query: queryParameters,
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
location.href = basePath;
|
|
38
|
+
})();
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<style>
|
|
42
|
+
body {
|
|
43
|
+
width: 100vw;
|
|
44
|
+
height: 100vh;
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-direction: column;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
align-items: center;
|
|
49
|
+
}
|
|
50
|
+
</style>
|
|
51
|
+
</head>
|
|
52
|
+
<body>
|
|
53
|
+
<h1>404 | Page not found</h1>
|
|
54
|
+
<a href="/">go home</a>
|
|
55
|
+
</body>
|
|
56
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="@total-typescript/ts-reset" />
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
declare module 'virtual:aerogel' {
|
|
2
|
+
export interface VirtualAerogel {
|
|
3
|
+
environment: 'production' | 'development' | 'testing' | 'test' | string;
|
|
4
|
+
basePath?: string;
|
|
5
|
+
sourceUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const virtual: VirtualAerogel;
|
|
9
|
+
|
|
10
|
+
export default virtual;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare module 'virtual:aerogel-solid' {
|
|
14
|
+
export interface VirtualAerogelSolid {
|
|
15
|
+
clientID?: ClientIDDocument;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ClientIDDocument {
|
|
19
|
+
'@context': 'https://www.w3.org/ns/solid/oidc-context.jsonld';
|
|
20
|
+
client_id: string;
|
|
21
|
+
client_name: string;
|
|
22
|
+
redirect_uris: string[];
|
|
23
|
+
client_uri: string;
|
|
24
|
+
logo_uri?: string;
|
|
25
|
+
scope: string;
|
|
26
|
+
grant_types: string[];
|
|
27
|
+
response_types: string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const virtual: VirtualAerogelSolid;
|
|
31
|
+
|
|
32
|
+
export default virtual;
|
|
33
|
+
}
|
package/tsconfig.json
ADDED
package/vite.config.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
test: {
|
|
6
|
+
clearMocks: true,
|
|
7
|
+
setupFiles: ['./src/testing/setup.ts'],
|
|
8
|
+
},
|
|
9
|
+
resolve: {
|
|
10
|
+
alias: {
|
|
11
|
+
'@': resolve(__dirname, './src'),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
});
|