Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support subdomains #100

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,20 @@ Can access the default router.

Can disable/enable the `pages/` directory into Nuxt.

### `rootDomain`

- Type: `String`
- Default: `'null'`

Directory name for the pages of root-domain.

### `subDomains`

- Type: `String[]`
- Default: `[]`

List of directories to hold te pages for your subdomains.

## Usage

This module, by default, disable the `pages/` directory into Nuxt and will use a `router.js` file at your `srcDir` directory:
Expand Down Expand Up @@ -136,13 +150,21 @@ If you use the module with `{ keepDefaultRouter: true }`, you can access the def

:warning: If you are using Nuxt `< 2.15.0`, the parameter `config` is not available.

:warning: If you are using Nuxt Router `< 1.7.0`, the parameter `getRoutesDomainOrSubdomain` is not available.

```js
export function createRouter(ssrContext, createDefaultRouter, routerOptions, config) {
const options = routerOptions ? routerOptions : createDefaultRouter(ssrContext, config).options
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export function createRouter(ssrContext, createDefaultRouter, routerOptions, config, getRoutesDomainOrSubdomain) {
const options = routerOptions || createDefaultRouter(ssrContext, config).options
const routes = getRoutesDomainOrSubdomain ? getRoutesDomainOrSubdomain(ssrContext, options.routes) : options.routes

return new Router({
...options,
routes: fixRoutes(options.routes)
routes: fixRoutes(routes)
})
}

Expand Down
19 changes: 14 additions & 5 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export interface ModuleOptions {
fileName: string;
keepDefaultRouter?: boolean;
parsePages?: boolean;
rootDomain?: string;
subDomains?: string[];
}

const CONFIG_KEY = 'routerModule'
Expand All @@ -20,7 +22,9 @@ const nuxtModule: Module<ModuleOptions> = function (moduleOptions) {
const DEFAULTS: ModuleOptions = {
path: this.options.srcDir,
fileName: 'router.js',
keepDefaultRouter: false
keepDefaultRouter: false,
rootDomain: null,
subDomains: []
}

const options: ModuleOptions = defu(
Expand All @@ -37,18 +41,23 @@ const nuxtModule: Module<ModuleOptions> = function (moduleOptions) {
const routerFilePath = resolve(options.path, options.fileName)

// Check if router file path is defined
if (!existsSync(routerFilePath)) {
if (!existsSync(routerFilePath) && !options.keepDefaultRouter) {
logger.warn(`No \`${options.fileName}\` file found in \`${options.path}\`.`)
return

options.keepDefaultRouter = true
}

// Add plugin to import router file path as the main template for routing
this.addPlugin({
src: resolve(__dirname, '../templates/plugin.js'),
fileName: 'router.js',
options: {
routerFilePath: relative(this.options.buildDir, routerFilePath).replace(/\/+|\\+/g, '/'),
keepDefaultRouter: options.keepDefaultRouter
routerFilePath: existsSync(routerFilePath)
? relative(this.options.buildDir, routerFilePath).replace(/\/+|\\+/g, '/')
: null,
keepDefaultRouter: options.keepDefaultRouter,
rootDomain: options.rootDomain,
subDomains: options.subDomains
}
})

Expand Down
90 changes: 87 additions & 3 deletions templates/plugin.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,96 @@
import { createRouter as customCreateRouter } from '<%= options.routerFilePath %>'

<% if (options.keepDefaultRouter) { %>
import { createRouter as createDefaultRouter, routerOptions } from './defaultRouter'
<% } else { %>
const createDefaultRouter = null
const routerOptions = null
<% } %>

<% if (options.routerFilePath) { %>
import { createRouter as customCreateRouter } from '<%= options.routerFilePath %>'
<% } else { %>
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

const customCreateRouter = (ssrContext, createDefaultRouter, routerOptions, config, getRoutesDomainOrSubdomain) => {
const options = routerOptions || createDefaultRouter(ssrContext, config).options
const routes = getRoutesDomainOrSubdomain(ssrContext, options.routes)

return new Router({
...options,
routes
})
}
<% } %>

export function createRouter(ssrContext, config) {
return customCreateRouter(ssrContext, createDefaultRouter, routerOptions, config)
return customCreateRouter(ssrContext, createDefaultRouter, routerOptions, config, getRoutesDomainOrSubdomain)
}

const getRoutesDomainOrSubdomain = (ssrContext, routes) => {
const rootDomain = '<%= options.rootDomain %>'
const subdomains = [<%= options.subDomains.map(subdomain => `'${subdomain}'`).join(', ') %>]
const routesDirectory = getRoutesDirectory(ssrContext, rootDomain, subdomains)

if (!routesDirectory) {
return routes
}

return routes
.filter(route => {
return !routeIsUnderDirectory(route, [...subdomains, rootDomain].filter(domain => domain !== routesDirectory))
})
.map((route) => {
if (!routeIsUnderDirectory(route, routesDirectory)) {
return route
}

return {
...route,
path: route.path.substr(routesDirectory.length + 1) || '/',
name: route.name.substr(routesDirectory.length + 1) || 'index'
}
})
}

const getRoutesDirectory = (ssrContext, rootDomain, subdomains) => {
let routesDirectory = null

if (!rootDomain || !subdomains || !subdomains.length) {
return routesDirectory
}

if (process.server && ssrContext && ssrContext.nuxt && ssrContext.req) {
const req = ssrContext.req

// get the subdomain from the request host
const matcher = req.headers.host.match(/^(\w+(-\w+)?)\.(localhost|\w+(-\w+)?)(\.\w+)?/)
routesDirectory = matcher[1] || matcher[0]

// if the subdomain is not in the list of user provided domains, set the root directory to root - domain given by the user
routesDirectory = subdomains.includes(routesDirectory) ? routesDirectory : rootDomain

// Save to the object that will be sent to the client as inline-script
ssrContext.nuxt.routesDirectory = routesDirectory
}

// Get what we saved on SSR
if (process.client && window.__NUXT__ && window.__NUXT__.routesDirectory) {
routesDirectory = window.__NUXT__.routesDirectory
}

return routesDirectory
}

const routeIsUnderDirectory = (route, directory) => {
const dirs = Array.isArray(directory) ? directory : [directory]

for (const dir of dirs) {
if (route.path === '/' + dir || route.path.startsWith('/' + dir + '/')) {
return true
}
}

return false
}