Skip to content

Commit

Permalink
refactor: module improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
ricardogobbosouza committed Mar 31, 2021
1 parent 5193ba1 commit 4cef1a8
Show file tree
Hide file tree
Showing 18 changed files with 286 additions and 130 deletions.
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ dist
coverage

# Plugin
templates/plugin.js
src/runtime/*.js
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
preset: '@nuxt/test-utils',
collectCoverageFrom: ['src/**']
collectCoverageFrom: ['src/**', '!src/runtime/**']
}
14 changes: 8 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
"nuxt",
"module",
"nuxt-module",
"laravel",
"echo"
"laravel-echo",
"echo",
"laravel"
],
"repository": "nuxt-community/laravel-echo-module",
"license": "MIT",
Expand All @@ -19,13 +20,13 @@
"require": "./dist/module.js",
"import": "./dist/module.es.js"
},
"./": "./"
"./runtime/*": "./dist/runtime/*",
"./package.json": "./package.json"
},
"main": "./dist/module.js",
"types": "./dist/module.d.ts",
"files": [
"dist",
"templates"
"dist"
],
"scripts": {
"build": "siroc build",
Expand All @@ -34,12 +35,13 @@
"test": "jest"
},
"dependencies": {
"defu": "^3.2.2",
"laravel-echo": "^1.10.0"
},
"devDependencies": {
"@babel/preset-typescript": "latest",
"@nuxt/test-utils": "latest",
"@nuxt/types": "latest",
"@nuxt/typescript-build": "latest",
"@nuxtjs/eslint-config-typescript": "latest",
"@types/jest": "latest",
"@types/node": "latest",
Expand Down
55 changes: 27 additions & 28 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
import { resolve } from 'path'
import { resolve, join } from 'path'
import { existsSync } from 'fs'
import defu from 'defu'
import { Module } from '@nuxt/types'
import { NuxtOptionsPlugin } from '@nuxt/types/config/plugin'
import { name, version } from '../package.json'

const CONFIG_KEY = 'echo'

export interface ModuleOptions {
broadcaster?: string,
plugins?: string[],
authModule?: boolean,
connectOnLogin?: boolean,
disconnectOnLogout?: boolean,
}
import { ModuleOptions } from './runtime/types'

const DEFAULTS: ModuleOptions = {
broadcaster: 'null',
encrypted: false,
authModule: false,
connectOnLogin: true,
disconnectOnLogout: true
Expand All @@ -24,29 +17,40 @@ const DEFAULTS: ModuleOptions = {
const nuxtModule: Module<ModuleOptions> = function (moduleOptions) {
this.nuxt.hook('builder:extendPlugins', (plugins: NuxtOptionsPlugin[]) => {
const options: ModuleOptions = defu(
this.options[CONFIG_KEY] || {},
this.options.echo || {},
moduleOptions,
DEFAULTS
)

// Copy echo plugin
const { dst } = this.addTemplate({
src: resolve(__dirname, '../templates/plugin.js'),
fileName: `${CONFIG_KEY}.js`,
const runtimeDir = resolve(__dirname, 'runtime')
this.options.alias['~echo'] = runtimeDir
this.options.build.transpile.push(runtimeDir, 'laravel-echo', 'defu')

const optionsPath: string = this.nuxt.resolver.resolveAlias(options.optionsPath ||
join(this.options.dir!.app || 'app', 'laravel-echo', 'options.js'))

// Register options template
this.addTemplate({
fileName: `laravel-echo/options.${optionsPath && optionsPath.endsWith('ts') ? 'ts' : 'js'}`,
src: existsSync(optionsPath) ? optionsPath : resolve(__dirname, './runtime/options.js'),
options
})

plugins.push({
ssr: false,
src: resolve(this.options.buildDir, dst)
// Copy echo plugin
const { dst } = this.addTemplate({
src: resolve(__dirname, './runtime/plugin.js'),
fileName: 'laravel-echo/plugin.js',
options: {
broadcaster: options.broadcaster,
encrypted: options.encrypted
}
})

plugins.push(resolve(this.options.buildDir, dst))

// Extend echo with plugins
if (options.plugins) {
options.plugins.forEach(p => plugins.push({
ssr: false,
src: p
}))
options.plugins.forEach(p => plugins.push(p))

delete options.plugins
}
Expand All @@ -55,9 +59,4 @@ const nuxtModule: Module<ModuleOptions> = function (moduleOptions) {

;(nuxtModule as any).meta = { name, version }

declare module '@nuxt/types' {
interface NuxtConfig { [CONFIG_KEY]?: ModuleOptions } // Nuxt 2.14+
interface Configuration { [CONFIG_KEY]?: ModuleOptions } // Nuxt 2.9 - 2.13
}

export default nuxtModule
70 changes: 70 additions & 0 deletions src/runtime/echo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import BaseEcho from 'laravel-echo'
import defu from 'defu'
import { Context } from '@nuxt/types'
import { ModuleOptions } from './types'

export class Echo extends BaseEcho {
ctx: Context;
config: Partial<ModuleOptions>;

constructor (ctx: Context, config: Partial<ModuleOptions> = {}) {
// when enabled authModule, start broadcaster with null
// because laravel-echo auto connect https://github.com/laravel/echo/blob/master/src/echo.ts#L23
// the connection should be made only when the user logs in
super(defu(config.authModule && config.connectOnLogin ? { broadcaster: 'null' } : {}, config))

this.ctx = ctx
this.ctx.$config = this.ctx.$config || {} // fallback for Nuxt < 2.13
this.config = defu(this.ctx.$config.echo || {}, { auth: { headers: {} } }, config)
}

async init () {
this.options.auth = this.options.auth || {}
this.options.auth.headers = await this.getHeaders()
this.watchAuthState()
}

async getHeaders () {
let headers = this.config.auth.headers || {}

if (typeof headers === 'function') {
headers = await headers(this.ctx)
}

if (this.config.authModule && this.ctx.app.$auth) {
const strategy = this.ctx.app.$auth.strategy

if (strategy.options.name === 'laravelSanctum') {
headers.referer = location.origin
headers['X-XSRF-TOKEN'] = this.ctx.app.$auth.$storage.getCookies()['XSRF-TOKEN']
} else {
const tokenName = strategy.options.token.name || 'Authorization'
const token = strategy.token.get()

if (token) {
headers[tokenName] = token
}
}
}

return defu(headers, this.options.auth.headers)
}

watchAuthState () {
if (this.config.authModule && this.ctx.app.$auth) {
this.ctx.app.$auth.$storage.watchState('loggedIn', async (loggedIn: boolean) => {
this.options.auth.headers = await this.getHeaders()

if (this.config.connectOnLogin && loggedIn) {
// set broadcaster when user logged in
this.options.broadcaster = this.config.broadcaster
this.connect()
}

if (this.config.disconnectOnLogout && !loggedIn && this.connector) {
this.disconnect()
}
}).bind(this)
}
}
}
35 changes: 35 additions & 0 deletions src/runtime/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Echo } from './echo'
import { ModuleOptions } from './types'

declare module '@nuxt/types' {
interface NuxtAppOptions {
$echo: Echo;
}

interface Context {
$echo: Echo;
}

interface NuxtConfig {
echo?: Partial<ModuleOptions>
}

interface Configuration {
echo?: Partial<ModuleOptions>
}
}

declare module 'vue/types/vue' {
interface Vue {
$echo: Echo
}
}

declare module 'vuex/types/index' {
// eslint-disable-next-line no-unused-vars,@typescript-eslint/no-unused-vars
interface Store<S> {
$echo: Echo
}
}

export * from './echo'
1 change: 1 addition & 0 deletions src/runtime/options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default <%= serializeFunction(options) %>
29 changes: 29 additions & 0 deletions src/runtime/plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import options from './options'
import { Echo } from '~echo'

/**
* @type {import('@nuxt/types').Plugin}
*/
export default async function (ctx, inject) {
const echoOptions = typeof options === 'function' ? await options(ctx) : options

if (process.client) {
<% if (options.broadcaster === 'pusher' && !options.encrypted) { %>
if (!window.Pusher) window.Pusher = require('pusher-js')
<% } %>

<% if (options.broadcaster === 'pusher' && options.encrypted) { %>
if (!window.Pusher) window.Pusher = require('pusher-js/with-encryption')
<% } %>

<% if (options.broadcaster === 'socket.io') { %>
if (!window.io) window.io = require('socket.io-client')
<% } %>
}

const echo = new Echo(ctx, echoOptions)
await echo.init()

inject('echo', echo)
ctx.$echo = echo
}
11 changes: 11 additions & 0 deletions src/runtime/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type EchoOptions = Record<string, any>

export interface ModuleOptions extends EchoOptions {
broadcaster?: string,
encrypted?: boolean,
plugins?: string[],
authModule?: boolean,
connectOnLogin?: boolean,
disconnectOnLogout?: boolean,
optionsPath?: string,
}
75 changes: 0 additions & 75 deletions templates/plugin.js

This file was deleted.

3 changes: 2 additions & 1 deletion test/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { setupTest, getNuxt, get, url } from '@nuxt/test-utils'
describe('basic', () => {
setupTest({
server: true,
fixture: 'fixture/basic'
fixture: 'fixture/basic',
configFile: 'nuxt.config.ts'
})

test('echo should be defined', async () => {
Expand Down
5 changes: 0 additions & 5 deletions test/fixture/basic/nuxt.config.js

This file was deleted.

9 changes: 9 additions & 0 deletions test/fixture/basic/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

import { NuxtConfig } from '@nuxt/types'

export default <NuxtConfig> {
buildModules: [
'@nuxt/typescript-build',
'../../../src/module.ts'
]
}
Loading

0 comments on commit 4cef1a8

Please sign in to comment.