You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 4-6.RSVP
Socket
Book a DemoInstallSign in
Socket

vite-plugin-vue-layouts-next

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vite-plugin-vue-layouts-next - npm Package Compare versions

Comparing version

to
0.1.0

17

dist/index.d.ts

@@ -32,2 +32,7 @@ import { Plugin } from 'vite';

/**
* If set, wrap the route's internal component with a layout instead of adding a wrapping route
* @default false
*/
wrapComponent?: boolean;
/**
* Mode for importing layouts

@@ -37,6 +42,6 @@ */

}
type FileContainer = {
interface FileContainer {
path: string;
files: string[];
};
}
type UserOptions = Partial<Options>;

@@ -60,8 +65,12 @@ interface ResolvedOptions extends Options {

importMode?: 'sync' | 'async';
/**
* If set, wrap the route's internal component with a layout instead of adding a wrapping route
* @default false
*/
wrapComponent?: boolean;
}
declare function defaultImportMode(name: string): "sync" | "async";
declare function Layout(userOptions?: UserOptions): Plugin;
declare function ClientSideLayout(options?: clientSideOptions): Plugin;
export { ClientSideLayout, type FileContainer, type ResolvedOptions, type UserOptions, type clientSideOptions, Layout as default, defaultImportMode };
export { ClientSideLayout, type FileContainer, type ResolvedOptions, type UserOptions, type clientSideOptions, Layout as default };

@@ -34,13 +34,85 @@ "use strict";

ClientSideLayout: () => ClientSideLayout,
default: () => Layout,
defaultImportMode: () => defaultImportMode
default: () => Layout
});
module.exports = __toCommonJS(index_exports);
var import_node_path3 = require("path");
var import_node_process2 = __toESM(require("process"));
// src/clientSide.ts
var import_node_path2 = require("path");
// src/utils.ts
var import_node_path = require("path");
function normalizePath(path) {
var import_debug = __toESM(require("debug"));
var import_fast_glob = __toESM(require("fast-glob"));
function extensionsToGlob(extensions) {
return extensions.length > 1 ? `{${extensions.join(",")}}` : extensions[0] || "";
}
function normalizePath(str) {
return str.replace(/\\/g, "/");
}
var debug = (0, import_debug.default)("vite-plugin-layouts");
function resolveDirs(dirs, root) {
if (dirs === null)
return [];
const dirsArray = Array.isArray(dirs) ? dirs : [dirs];
const dirsResolved = [];
for (const dir of dirsArray) {
if (dir.includes("**")) {
const matches = import_fast_glob.default.sync(dir, { onlyDirectories: true });
for (const match of matches)
dirsResolved.push(normalizePath((0, import_node_path.resolve)(root, match)));
} else {
dirsResolved.push(normalizePath((0, import_node_path.resolve)(root, dir)));
}
}
return dirsResolved;
}
function addIndentation(code, indent) {
return code.replace(/\n[ \t]+/g, `
${" ".repeat(indent)}`);
}
// src/layoutReturn.ts
var returnLayoutRoute = (
/* js */
`
/** @type {import('vue-router').RouteRecordRaw} */
const layoutRoute = {
path: route.path,
component: layouts[layout],
meta: { ...route.meta, isLayout: true },
// Handle root path specially to avoid infinite nesting
children: top && route.path === '/'
? [route]
: [{ ...route, path: '', meta: { ...route.meta, isLayout: false } }]
}
return layoutRoute
`
);
var returnLayoutComponent = (
/* js */
`
if (!route.component) {
return route
}
/** @type {import('vue-router').RouteRecordRaw} */
const wrappedRoute = {
...route,
component: h('div', [
h(layouts[layout].layout),
h(layouts[layout].isSync ? defineComponent(() => route.component()) : defineAsyncComponent(() => route.component())),
]),
meta: {
...route.meta,
isLayout: true
}
}
return wrappedRoute
`
);
// src/clientSide.ts
function normalizePath2(path) {
path = path.startsWith("/") ? path : `/${path}`;
return import_node_path.posix.normalize(path);
return import_node_path2.posix.normalize(path);
}

@@ -53,5 +125,7 @@ async function createVirtualGlob(target, isSync) {

const { layoutDir, defaultLayout, importMode } = options;
const normalizedTarget = normalizePath(layoutDir);
const normalizedTarget = normalizePath2(layoutDir);
const isSync = importMode === "sync";
return `
return (
/* js */
`
export const createGetRoutes = (router, withLayout = false) => {

@@ -69,5 +143,5 @@ const routes = router.getRoutes()

const modules = ${await createVirtualGlob(
normalizedTarget,
isSync
)}
normalizedTarget,
isSync
)}

@@ -85,32 +159,15 @@ Object.entries(modules).forEach(([name, module]) => {

if (top) {
// unplugin-vue-router adds a top-level route to the routing group, which we should skip.
const skipLayout = !route.component && route.children?.find(r => (r.path === '' || r.path === '/') && r.meta?.isLayout)
const layout = route.meta?.layout ?? '${options.defaultLayout}'
if (skipLayout) {
return route
}
if (route.meta?.layout !== false) {
return {
path: route.path,
component: layouts[route.meta?.layout || '${defaultLayout}'],
children: route.path === '/' ? [route] : [{...route, path: ''}],
meta: {
isLayout: true
}
}
}
}
const skipLayout = top
&& !route.component
&& route.children?.find(r => (r.path === '' || r.path === '/') && r.meta?.isLayout);
if (route.meta?.layout) {
return {
path: route.path,
component: layouts[route.meta?.layout],
children: [ {...route, path: ''} ],
meta: {
isLayout: true
}
}
if (skipLayout) {
return route
}
if (layout && layouts[layout]) {
${addIndentation(options.wrapComponent ? returnLayoutComponent : returnLayoutRoute, 8)}
}

@@ -122,37 +179,33 @@ return route

return deepSetupLayout(routes)
}`;
}`
);
}
// src/files.ts
var import_fast_glob2 = __toESM(require("fast-glob"));
// src/utils.ts
var import_node_path2 = require("path");
var import_debug = __toESM(require("debug"));
var import_fast_glob = __toESM(require("fast-glob"));
function extensionsToGlob(extensions) {
return extensions.length > 1 ? `{${extensions.join(",")}}` : extensions[0] || "";
// src/defaults.ts
var import_node_process = __toESM(require("process"));
function defaultImportMode(name) {
if (import_node_process.default.env.VITE_SSG)
return "sync";
return name === "default" ? "sync" : "async";
}
function normalizePath2(str) {
return str.replace(/\\/g, "/");
function resolveOptions(userOptions) {
return Object.assign(
{
defaultLayout: "default",
layoutsDirs: "src/layouts",
pagesDirs: "src/pages",
extensions: ["vue"],
exclude: [],
wrapComponent: false,
importMode: defaultImportMode
},
userOptions
);
}
var debug = (0, import_debug.default)("vite-plugin-layouts");
function resolveDirs(dirs, root) {
if (dirs === null)
return [];
const dirsArray = Array.isArray(dirs) ? dirs : [dirs];
const dirsResolved = [];
for (const dir of dirsArray) {
if (dir.includes("**")) {
const matches = import_fast_glob.default.sync(dir, { onlyDirectories: true });
for (const match of matches)
dirsResolved.push(normalizePath2((0, import_node_path2.resolve)(root, match)));
} else {
dirsResolved.push(normalizePath2((0, import_node_path2.resolve)(root, dir)));
}
}
return dirsResolved;
}
// src/generateLayouts.ts
var import_node_path4 = require("path");
// src/files.ts
var import_fast_glob2 = __toESM(require("fast-glob"));
async function getFilesFromPath(path, options) {

@@ -174,3 +227,3 @@ const {

// src/importCode.ts
var import_path = require("path");
var import_node_path3 = require("path");
function getImportCode(files, options) {

@@ -183,15 +236,21 @@ const imports = [];

const path = __.path.substr(0, 1) === "/" ? `${__.path}/${file}` : `/${__.path}/${file}`;
const parsed = (0, import_path.parse)(file);
const name = (0, import_path.join)(parsed.dir, parsed.name).replace(/\\/g, "/");
const parsed = (0, import_node_path3.parse)(file);
const name = (0, import_node_path3.join)(parsed.dir, parsed.name).replace(/\\/g, "/");
if (options.importMode(name) === "sync") {
const variable = `__layout_${id}`;
head.push(`import ${variable} from '${path}'`);
imports.push(`'${name}': ${variable},`);
imports.push(
/* js */
`'${name}': { layout: ${variable}, isSync: true },`
);
id += 1;
} else {
imports.push(`'${name}': () => import('${path}'),`);
imports.push(
/* js */
`'${name}': { layout: () => import('${path}'), isSync: false },`
);
}
}
}
const importsCode = `
let importsCode = `
${head.join("\n")}

@@ -201,2 +260,20 @@ export const layouts = {

}`;
if (options.wrapComponent) {
const vueImports = [];
const nullImports = [];
vueImports.push("h");
if (id > 0)
vueImports.push("defineAsyncComponent");
else
nullImports.push("defineAsyncComponent");
if (imports.length - id > 0)
vueImports.push("defineComponent");
else
nullImports.push("defineComponent");
importsCode = `
import { ${vueImports.join(", ")} } from 'vue'
${importsCode}
${nullImports.map((v) => `const ${v} = null`).join("\n")}
`;
}
return importsCode;

@@ -207,3 +284,5 @@ }

function getClientCode(importCode, options) {
const code = `
const code = (
/* js */
`
${importCode}

@@ -225,31 +304,14 @@ export const createGetRoutes = (router, withLayout = false) => {

if (top) {
// unplugin-vue-router adds a top-level route to the routing group, which we should skip.
const skipLayout = !route.component && route.children?.find(r => (r.path === '' || r.path === '/') && r.meta?.isLayout)
const layout = route.meta?.layout ?? '${options.defaultLayout}'
if (skipLayout) {
return route
}
const skipLayout = top
&& !route.component
&& route.children?.find(r => (r.path === '' || r.path === '/') && r.meta?.isLayout);
if (route.meta?.layout !== false) {
return {
path: route.path,
component: layouts[route.meta?.layout || '${options.defaultLayout}'],
children: route.path === '/' ? [route] : [{...route, path: ''}],
meta: {
isLayout: true
}
}
}
if (skipLayout) {
return route
}
if (route.meta?.layout) {
return {
path: route.path,
component: layouts[route.meta?.layout],
children: [ {...route, path: ''} ],
meta: {
isLayout: true
}
}
if (layout && layouts[layout]) {
${addIndentation(options.wrapComponent ? returnLayoutComponent : returnLayoutRoute, 8)}
}

@@ -264,3 +326,4 @@

}
`;
`
);
return code;

@@ -270,23 +333,19 @@ }

// src/generateLayouts.ts
async function generateLayouts(layoutDirs, options, config) {
const container = [];
for (const dir of layoutDirs) {
const layoutsDirPath = dir.substr(0, 1) === "/" ? normalizePath(dir) : normalizePath((0, import_node_path4.resolve)(config.root, dir));
const _f = await getFilesFromPath(layoutsDirPath, options);
container.push({ path: layoutsDirPath, files: _f });
}
const importCode = getImportCode(container, options);
const clientCode = RouteLayout_default(importCode, options);
debug("Client code: %O", clientCode);
return clientCode;
}
// src/index.ts
var MODULE_IDS = ["layouts-generated", "virtual:generated-layouts"];
var MODULE_ID_VIRTUAL = "/@vite-plugin-vue-layouts-next/generated-layouts";
function defaultImportMode(name) {
if (process.env.VITE_SSG)
return "sync";
return name === "default" ? "sync" : "async";
}
function resolveOptions(userOptions) {
return Object.assign(
{
defaultLayout: "default",
layoutsDirs: "src/layouts",
pagesDirs: "src/pages",
extensions: ["vue"],
exclude: [],
importMode: defaultImportMode
},
userOptions
);
}
function Layout(userOptions = {}) {

@@ -325,3 +384,3 @@ if (canEnableClientLayout(userOptions)) {

const updateVirtualModule = (path) => {
path = normalizePath2(path);
path = normalizePath(path);
if (pagesDirs.length === 0 || pagesDirs.some((dir) => path.startsWith(dir)) || layoutDirs.some((dir) => path.startsWith(dir))) {

@@ -348,13 +407,3 @@ debug("reload", path);

if (id === MODULE_ID_VIRTUAL) {
const container = [];
for (const dir of layoutDirs) {
const layoutsDirPath = dir.substr(0, 1) === "/" ? normalizePath2(dir) : normalizePath2((0, import_node_path3.resolve)(config.root, dir));
debug("Loading Layout Dir: %O", layoutsDirPath);
const _f = await getFilesFromPath(layoutsDirPath, options);
container.push({ path: layoutsDirPath, files: _f });
}
const importCode = getImportCode(container, options);
const clientCode = RouteLayout_default(importCode, options);
debug("Client code: %O", clientCode);
return clientCode;
return generateLayouts(layoutDirs, options, config);
}

@@ -368,3 +417,4 @@ }

defaultLayout = "default",
importMode = process.env.VITE_SSG ? "sync" : "async"
importMode = import_node_process2.default.env.VITE_SSG ? "sync" : "async",
wrapComponent = false
} = options || {};

@@ -383,3 +433,4 @@ return {

importMode,
defaultLayout
defaultLayout,
wrapComponent
});

@@ -400,4 +451,3 @@ }

0 && (module.exports = {
ClientSideLayout,
defaultImportMode
ClientSideLayout
});
{
"name": "vite-plugin-vue-layouts-next",
"version": "0.0.14",
"version": "0.1.0",
"description": "Router-based layout plugin for Vite and Vue, supports the latest versions.",

@@ -57,3 +57,8 @@ "author": "loicduong <npm@relate.dev>",

"example:build-vitesse": "npm -C examples/vitesse run build",
"example:serve-vitesse": "npm -C examples/vitesse run preview"
"example:serve-vitesse": "npm -C examples/vitesse run preview",
"test": "vitest run",
"test-watch": "vitest run --watch",
"test-update": "vitest run --update",
"test:unit": "vitest run --exclude test/e2e/**",
"test:e2e": "vitest run --exclude test/unit/**"
},

@@ -72,13 +77,14 @@ "peerDependencies": {

"@types/debug": "^4.1.12",
"@types/node": "^22.15.2",
"@types/node": "^22.15.3",
"cross-env": "^7.0.3",
"eslint": "^9.25.1",
"eslint": "^9.26.0",
"eslint-plugin-format": "^1.0.1",
"rollup": "^4.40.0",
"rollup": "^4.40.1",
"tsup": "^8.4.0",
"typescript": "^5.8.3",
"vite": "^6.3.3",
"vue": "^3.5.13",
"vue-router": "^4.5.1"
"vite": "catalog:",
"vitest": "catalog:",
"vue": "catalog:",
"vue-router": "catalog:"
}
}

@@ -53,4 +53,4 @@ # vite-plugin-vue-layouts-next

```js
import { setupLayouts } from 'virtual:generated-layouts'
import { createRouter } from 'vue-router'
import { setupLayouts } from 'virtual:generated-layouts'
import generatedRoutes from '~pages'

@@ -69,4 +69,4 @@

```js
import { setupLayouts } from 'virtual:generated-layouts'
import { createRouter } from 'vue-router'
import { setupLayouts } from 'virtual:generated-layouts'
import { routes } from 'vue-router/auto-routes'

@@ -101,2 +101,3 @@

defaultLayout?: string
wrapComponent?: boolean
importMode?: (name: string) => 'sync' | 'async'

@@ -170,2 +171,8 @@ }

### wrapComponent
If set to `true`, wraps the route's internal component with a layout instead of adding a wrapping route. This can be useful for better performance and simpler route structure. Especially if you have extensive routes, you can use this to avoid the overhead of adding a wrapping route for each page and maintain an easily parsable route structure.
**Default:** `false`
## How it works

@@ -172,0 +179,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet