From b2eee1772eb87c85eb3b6a0cd2ff14a049e76c04 Mon Sep 17 00:00:00 2001 From: Alexander Lichter Date: Wed, 19 Dec 2018 15:22:00 +0000 Subject: [PATCH] feat: add store module HMR (#4582) * feat: add store module HMR * fix: replace export with window.$nuxt Thanks to @pi0 for the suggestion :) * refactor: apply only in dev mode on client side * test: make store module test more descriptive * fix: clear modules to apply HMR * fix: remove console.log * fix: e2e tests * refactor: use void --- packages/vue-app/template/store.js | 122 ++++++++++++++++------------ test/e2e/basic.browser.test.js | 6 +- test/fixtures/basic/pages/store.vue | 10 ++- test/unit/basic.ssr.test.js | 6 +- 4 files changed, 82 insertions(+), 62 deletions(-) diff --git a/packages/vue-app/template/store.js b/packages/vue-app/template/store.js index 9ca04e3306..8efc54634f 100644 --- a/packages/vue-app/template/store.js +++ b/packages/vue-app/template/store.js @@ -3,73 +3,87 @@ import Vuex from 'vuex' Vue.use(Vuex) -const files = require.context('@/<%= dir.store %>', true, /^\.\/(?!<%= ignorePrefix %>)[^.]+\.(<%= extensions %>)$/) -const filenames = files.keys() - -// Store let storeData = {} -// Check if {dir.store}/index.js exists -const indexFilename = filenames.find(filename => filename.includes('./index.')) +let files; -if (indexFilename) { - storeData = getModule(indexFilename) -} +void function updateModules() { + files = require.context('@/<%= dir.store %>', true, /^\.\/(?!<%= ignorePrefix %>)[^.]+\.(<%= extensions %>)$/) + const filenames = files.keys() -// If store is not an exported method = modules store -if (typeof storeData !== 'function') { - // Store modules - if (!storeData.modules) { - storeData.modules = {} + // Check if {dir.store}/index.js exists + const indexFilename = filenames.find(filename => filename.includes('./index.')) + + if (indexFilename) { + storeData = getModule(indexFilename) } - for (const filename of filenames) { - let name = filename.replace(/^\.\//, '').replace(/\.(<%= extensions %>)$/, '') - if (name === 'index') continue - - const namePath = name.split(/\//) - - name = namePath[namePath.length - 1] - if (['state', 'getters', 'actions', 'mutations'].includes(name)) { - const module = getModuleNamespace(storeData, namePath, true) - appendModule(module, filename, name) - continue + // If store is not an exported method = modules store + if (typeof storeData !== 'function') { + // Store modules + if (!storeData.modules || module.hot) { + storeData.modules = {} } - // If file is foo/index.js, it should be saved as foo - const isIndex = (name === 'index') - if (isIndex) { - namePath.pop() - } + for (const filename of filenames) { + let name = filename.replace(/^\.\//, '').replace(/\.(<%= extensions %>)$/, '') + if (name === 'index') continue - const module = getModuleNamespace(storeData, namePath) - const fileModule = getModule(filename) + const namePath = name.split(/\//) - name = namePath.pop() - module[name] = module[name] || {} - - // if file is foo.js, existing properties take priority - // because it's the least specific case - if (!isIndex) { - module[name] = Object.assign({}, fileModule, module[name]) - module[name].namespaced = true - continue - } - - // if file is foo/index.js we want to overwrite properties from foo.js - // but not from appended mods like foo/actions.js - const appendedMods = {} - if (module[name].appends) { - appendedMods.appends = module[name].appends - for (const append of module[name].appends) { - appendedMods[append] = module[name][append] + name = namePath[namePath.length - 1] + if (['state', 'getters', 'actions', 'mutations'].includes(name)) { + const module = getModuleNamespace(storeData, namePath, true) + appendModule(module, filename, name) + continue } - } - module[name] = Object.assign({}, module[name], fileModule, appendedMods) - module[name].namespaced = true + // If file is foo/index.js, it should be saved as foo + const isIndex = (name === 'index') + if (isIndex) { + namePath.pop() + } + + const module = getModuleNamespace(storeData, namePath) + const fileModule = getModule(filename) + + name = namePath.pop() + module[name] = module[name] || {} + + // if file is foo.js, existing properties take priority + // because it's the least specific case + if (!isIndex) { + module[name] = Object.assign({}, fileModule, module[name]) + module[name].namespaced = true + continue + } + + // if file is foo/index.js we want to overwrite properties from foo.js + // but not from appended mods like foo/actions.js + const appendedMods = {} + if (module[name].appends) { + appendedMods.appends = module[name].appends + for (const append of module[name].appends) { + appendedMods[append] = module[name][append] + } + } + + module[name] = Object.assign({}, module[name], fileModule, appendedMods) + module[name].namespaced = true + } + // If the environment supports hot reloading... + <% if (isDev) { %> + if (process.client && module.hot) { + // Whenever any Vuex module is updated... + module.hot.accept(files.id, () => { + // Update `root.modules` with the latest definitions. + updateModules() + // Trigger a hot update in the store. + window.<%= globals.nuxt %>.$store.hotUpdate({ modules: storeData.modules }) + }) + }<% } %> } -} +}() // createStore export const createStore = storeData instanceof Function ? storeData : () => { diff --git a/test/e2e/basic.browser.test.js b/test/e2e/basic.browser.test.js index 6648db68ce..0b7a402ea5 100644 --- a/test/e2e/basic.browser.test.js +++ b/test/e2e/basic.browser.test.js @@ -78,8 +78,10 @@ describe('basic browser', () => { test('/store', async () => { await page.nuxt.navigate('/store') - expect(await page.$text('h1')).toBe('Vuex Nested Modules') - expect(await page.$text('p')).toBe('1') + expect(await page.$text('h1')).toBe('foo/bar/baz: Vuex Nested Modules') + expect(await page.$text('h2')).toBe('index/counter: 1') + expect(await page.$text('h3')).toBe('foo/blarg/getVal: 4') + expect(await page.$text('h4')).toBe('foo/bab/getBabVal: 10') }) test('/head', async () => { diff --git a/test/fixtures/basic/pages/store.vue b/test/fixtures/basic/pages/store.vue index d9d834cd51..17a661082a 100644 --- a/test/fixtures/basic/pages/store.vue +++ b/test/fixtures/basic/pages/store.vue @@ -1,12 +1,14 @@ diff --git a/test/unit/basic.ssr.test.js b/test/unit/basic.ssr.test.js index a04f74643e..848fc14b5b 100644 --- a/test/unit/basic.ssr.test.js +++ b/test/unit/basic.ssr.test.js @@ -70,8 +70,10 @@ describe('basic ssr', () => { test('/store', async () => { const { html } = await nuxt.server.renderRoute('/store') - expect(html).toContain('

Vuex Nested Modules

') - expect(html).toContain('

1

') + expect(html).toContain('

foo/bar/baz: Vuex Nested Modules

') + expect(html).toContain('

index/counter: 1

') + expect(html).toContain('

foo/blarg/getVal: 4

') + expect(html).toContain('

foo/bab/getBabVal: 10

') }) test('/head', async () => {