feat: add vite-layers
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
import { defineLayerConfig } from '../../../src/index.ts'
|
||||
|
||||
export default defineLayerConfig({
|
||||
name: 'brand',
|
||||
extends: ['../main'],
|
||||
features: { billing: false }, // brand drops the billing page entirely (DCE)
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>vite-layers — brand</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
BRAND_LOGO_OVERRIDE
|
||||
@@ -0,0 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
const title = 'BRAND_HEADER_OVERRIDE'
|
||||
const p2p = __FEATURES__.p2p
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>{{ title }}</header>
|
||||
<p v-if="p2p">p2p</p>
|
||||
</template>
|
||||
@@ -0,0 +1,4 @@
|
||||
// Brand has no bootstrap logic of its own — it reuses the base layer's entry.
|
||||
// `@/main.ts` resolves to *this* file first, but the layered resolver's self-skip
|
||||
// (super() semantics) falls through to the next layer, i.e. main/src/main.ts.
|
||||
import '@/main.ts'
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "./.vite-layers/tsconfig.json"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { buildViteConfig } from '../../../src/index.ts'
|
||||
|
||||
export default buildViteConfig(import.meta.dirname)
|
||||
@@ -0,0 +1,18 @@
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { defineLayerConfig } from '../../../src/index.ts'
|
||||
|
||||
export default defineLayerConfig({
|
||||
name: 'main',
|
||||
features: { billing: true, p2p: true },
|
||||
// The framework plugin lives in the layer config, not in vite-layers' core.
|
||||
vite: {
|
||||
plugins: [vue()],
|
||||
build: { rolldownOptions: { input: { main: '@/main.ts' } } },
|
||||
},
|
||||
// Per-layer tsconfig tweaks (merged across the stack, like Nuxt's typescript.tsConfig).
|
||||
tsConfig: { compilerOptions: { jsx: 'preserve', jsxImportSource: 'vue' } },
|
||||
|
||||
$production: {
|
||||
features: { p2p: false },
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>vite-layers — main</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
SHARED_FAVICON
|
||||
@@ -0,0 +1 @@
|
||||
MAIN_LOGO_SVG
|
||||
@@ -0,0 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const title = 'MAIN_HEADER'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>{{ title }}</header>
|
||||
</template>
|
||||
@@ -0,0 +1,27 @@
|
||||
import { createApp, defineAsyncComponent, h, shallowRef, type Component } from 'vue'
|
||||
const AppHeader = defineAsyncComponent(() => import('@/components/AppHeader.vue'))
|
||||
|
||||
// `__FEATURES__` is typed by the generated `.vite-layers/features.d.ts` — no manual `declare` needed.
|
||||
|
||||
// Pages are gated on build-time feature flags: a disabled page's dynamic import() is
|
||||
// statically dead, so its chunk is never emitted (per-brand dead-code elimination).
|
||||
const routes = [
|
||||
{ path: '/', component: () => import('@/pages/Home.vue') },
|
||||
...(__FEATURES__.billing
|
||||
? [{ path: '/billing', component: () => import('@/pages/Billing.vue') }]
|
||||
: []),
|
||||
]
|
||||
|
||||
// A tiny hash router so `routes` (and thus the gated import) is actually reachable.
|
||||
const current = shallowRef<Component | null>(null)
|
||||
async function navigate() {
|
||||
const path = location.hash.slice(1) || '/'
|
||||
const route = routes.find(r => r.path === path) ?? routes[0]
|
||||
current.value = route ? ((await route.component()).default as Component) : null
|
||||
}
|
||||
window.addEventListener('hashchange', navigate)
|
||||
void navigate()
|
||||
|
||||
createApp({
|
||||
render: () => h('div', [h(AppHeader), current.value ? h(current.value) : null]),
|
||||
}).mount('#app')
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<main>BILLING_PAGE_HEAVY_MARKER</main>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<main>Home page</main>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "./.vite-layers/tsconfig.json"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { buildViteConfig } from '../../../src/index.ts'
|
||||
|
||||
export default buildViteConfig(import.meta.dirname)
|
||||
Reference in New Issue
Block a user