From 9bd9ccf887e7aae91265e147e53398053ec4f666 Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Wed, 15 Apr 2020 21:42:27 +0200 Subject: [PATCH 01/10] feat: improvements --- .eslintignore | 3 +- .gitignore | 1 + example/components/ColorModePicker.vue | 6 +- jest.config.js | 3 +- lib/module.js | 46 ++++++------ lib/script.js | 72 +++++++++++++++++++ lib/templates/plugin.client.js | 72 +++++++++++++++++++ .../color-mode.server.js => plugin.server.js} | 12 ++-- lib/templates/plugins/color-mode.client.js | 57 --------------- lib/templates/script.js | 58 --------------- lib/utils.js | 18 +++++ package.json | 5 ++ rollup.config.js | 18 +++++ yarn.lock | 50 ++++++++++++- 14 files changed, 266 insertions(+), 155 deletions(-) create mode 100644 lib/script.js create mode 100644 lib/templates/plugin.client.js rename lib/templates/{plugins/color-mode.server.js => plugin.server.js} (71%) delete mode 100644 lib/templates/plugins/color-mode.client.js delete mode 100644 lib/templates/script.js create mode 100644 lib/utils.js create mode 100644 rollup.config.js diff --git a/.eslintignore b/.eslintignore index 0c32faf0..2b3360e6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,5 +4,4 @@ dist .nuxt coverage -# Plugin -lib/templates/* +*.min.js diff --git a/.gitignore b/.gitignore index 915f6ad5..990c8c67 100755 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ node_modules .DS_Store coverage dist +*.min.js diff --git a/example/components/ColorModePicker.vue b/example/components/ColorModePicker.vue index c0337b5f..2446e1c3 100644 --- a/example/components/ColorModePicker.vue +++ b/example/components/ColorModePicker.vue @@ -10,10 +10,10 @@

- + Color mode: {{ $colorMode.preference }} ({{ $colorMode.value }} mode detected) - +

@@ -39,7 +39,7 @@ export default { methods: { getClasses (color) { // Does not set classes on ssr preference is system (because we know them on client-side) - if (process.server && this.$colorMode.preference === 'system') { + if (this.$colorMode.unknown) { return {} } return { diff --git a/jest.config.js b/jest.config.js index 7635fb1a..f00a7d1c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,8 +2,7 @@ module.exports = { testEnvironment: 'node', collectCoverage: true, collectCoverageFrom: [ - 'lib/**/*.js', - '!lib/templates/**/*.js' + 'lib/**/*.js' ], moduleNameMapper: { '^~/(.*)$': '/lib/$1', diff --git a/lib/module.js b/lib/module.js index b7673fad..d2186ae9 100644 --- a/lib/module.js +++ b/lib/module.js @@ -1,12 +1,15 @@ -const { readFile } = require('fs').promises -const { resolve } = require('path') -const defu = require('defu') -const template = require('lodash.template') +import { resolve } from 'path' +import { promises as fsp } from 'fs' +import defu from 'defu' +import template from 'lodash.template' +import { addTemplates } from './utils' -module.exports = async function (moduleOptions) { +export default async function (moduleOptions) { const defaults = { preference: 'system', fallback: 'light', + hid: 'nuxt-color-mode-script', + globalName: '__NUXT_COLOR_MODE__', cookie: { key: 'nuxt-color-mode', options: { @@ -20,32 +23,23 @@ module.exports = async function (moduleOptions) { ...moduleOptions }, defaults) - // Add plugin to inject $colorMode - this.addPlugin({ - src: resolve(__dirname, 'templates', 'plugins', 'color-mode.client.js'), - fileName: 'plugins/color-mode.client.js', - options - }) - this.addPlugin({ - src: resolve(__dirname, 'templates', 'plugins', 'color-mode.server.js'), - fileName: 'plugins/color-mode.server.js', - options - }) + // Add all templates + const templatesDir = resolve(__dirname, 'templates') + await addTemplates.call(this, templatesDir, 'color-mode', options) // Add script to head to detect user or system preference before loading Nuxt (for SSR) - let script = await readFile(resolve(__dirname, 'templates', 'script.js'), 'utf-8') - script = template(script)({ options }) - // minify script for production - if (!this.options.dev) { - const { minify } = require('terser') - script = minify(script).code - } + const scriptPath = resolve(__dirname, 'script.min.js') + const scriptT = await fsp.readFile(scriptPath, 'utf-8') + const script = template(scriptT)({ options }) + this.options.head.script = this.options.head.script || [] this.options.head.script.push({ - hid: 'nuxt-color-mode-script', + hid: options.hid, innerHTML: script, pbody: true }) - this.options.head.__dangerouslyDisableSanitizersByTagID = this.options.head.__dangerouslyDisableSanitizersByTagID || {} - this.options.head.__dangerouslyDisableSanitizersByTagID['nuxt-color-mode-script'] = ['innerHTML'] + + const serializeProp = '__dangerouslyDisableSanitizersByTagID' + this.options.head[serializeProp] = this.options.head[serializeProp] || {} + this.options.head[serializeProp][options.hid] = ['innerHTML'] } diff --git a/lib/script.js b/lib/script.js new file mode 100644 index 00000000..78194023 --- /dev/null +++ b/lib/script.js @@ -0,0 +1,72 @@ +// Add dark / light detection that runs before loading Nuxt.js + +// Global variable minimizers +const w = window +const d = document +const de = d.documentElement + +const globalName = '__NUXT_COLOR_MODE__' +const knownColorSchemes = ['dark', 'light'] + +const preference = getCookie('<%= options.cookie.key %>') || '<%= options.preference %>' +const value = preference === 'system' ? getColorScheme() : preference + +addClass(value) + +w[globalName] = { + preference, + value, + getColorScheme, + addClass, + removeClass +} + +function getCookie (name) { + const nameEQ = name + '=' + const cookies = d.cookie.split(';') + + for (let i = 0; i < cookies.length; i++) { + let cookie = cookies[i] + while (cookie.charAt(0) === ' ') { + cookie = cookie.substring(1, cookie.length) + } + if (cookie.indexOf(nameEQ) === 0) { + return cookie.substring(nameEQ.length, cookie.length) + } + } + return null +} + +function addClass (value) { + const className = value + '-mode' + if (de.classList) { + de.classList.add(className) + } else { + de.className += ' ' + className + } +} + +function removeClass (value) { + const className = value + '-mode' + if (de.classList) { + de.classList.remove(className) + } else { + de.className = de.className.replace(new RegExp(className, 'g'), '') + } +} + +function prefersColorScheme (suffix) { + return w.matchMedia('(prefers-color-scheme' + suffix + ')') +} + +function getColorScheme () { + if (w.matchMedia && prefersColorScheme('').media !== 'not all') { + for (const colorScheme of knownColorSchemes) { + if (prefersColorScheme(':' + colorScheme).matches) { + return colorScheme + } + } + } + + return '<%= options.fallback %>' +} diff --git a/lib/templates/plugin.client.js b/lib/templates/plugin.client.js new file mode 100644 index 00000000..f16da55d --- /dev/null +++ b/lib/templates/plugin.client.js @@ -0,0 +1,72 @@ +import Vue from 'vue' +import { serialize } from 'cookie' + +const cookieKey = '<%= options.cookie.key %>' +const cookieOptions = JSON.parse('<%= JSON.stringify(options.cookie.options) %>') +const colorMode = window['<%= options.globalName %>'] + +export default function (ctx, inject) { + const $colorMode = new Vue({ + data: { + preference: colorMode.preference, + value: colorMode.value, + unknown: false + }, + watch: { + preference (preference) { + if (preference === 'system') { + this.value = colorMode.getColorScheme() + this._watchMedia() + } else { + this.value = preference + } + + this._storePreference(preference) + }, + value (newValue, oldValue) { + colorMode.removeClass(oldValue) + colorMode.addClass(newValue) + } + }, + created () { + if (this.preference === 'system') { + this._watchMedia() + } + if (window.localStorage) { + this._watchStorageChange() + } + }, + methods: { + _watchMedia () { + if (this._mediaWatcher || !window.matchMedia) { + return + } + + this._darkWatcher = window.matchMedia('(prefers-color-scheme: dark)') + this._darkWatcher.addListener((e) => { + if (this.preference === 'system') { + this.value = colorMode.getColorScheme() + } + }) + }, + _watchStorageChange () { + window.addEventListener('storage', (e) => { + if (e.key === cookieKey) { + this.preference = e.newValue + } + }) + }, + _storePreference (preference) { + // Cookies for SSR + document.cookie = serialize(cookieKey, preference, cookieOptions) + + // Local storage to sync with other tabs + if (window.localStorage) { + window.localStorage.setItem(cookieKey, preference) + } + } + } + }) + + inject('colorMode', $colorMode) +} diff --git a/lib/templates/plugins/color-mode.server.js b/lib/templates/plugin.server.js similarity index 71% rename from lib/templates/plugins/color-mode.server.js rename to lib/templates/plugin.server.js index 94e779e9..0d2c38bd 100644 --- a/lib/templates/plugins/color-mode.server.js +++ b/lib/templates/plugin.server.js @@ -3,19 +3,21 @@ import { parse } from 'cookie' const cookieKey = '<%= options.cookie.key %>' export default function (ctx, inject) { - // Read from cookie let preference = '<%= options.preference %>' + + // Try to read from cookies if (ctx.req) { // Check if cookie exist, otherwise TypeError: argument str must be a string const cookies = parse(ctx.req.headers.cookie || '') - - if (cookies[cookieKey] && cookies[cookieKey] !== preference) { + if (cookies[cookieKey]) { preference = cookies[cookieKey] } } + const colorMode = { - preference: preference, - value: preference + preference, + value: preference, + unknown: preference === 'system' /* TODO: Always true for static */ } inject('colorMode', colorMode) diff --git a/lib/templates/plugins/color-mode.client.js b/lib/templates/plugins/color-mode.client.js deleted file mode 100644 index 8ba2f242..00000000 --- a/lib/templates/plugins/color-mode.client.js +++ /dev/null @@ -1,57 +0,0 @@ -import Vue from 'vue' -import { parse, serialize } from 'cookie' - -const options = <%= JSON.stringify(options) %> -const wColorMode = window.__NUXT_COLOR_MODE__ - -export default function (ctx, inject) { - const colorMode = new Vue({ - data: { - preference: wColorMode.preference, - value: wColorMode.value - }, - watch: { - preference (preference) { - if (preference === 'system') { - this.value = wColorMode.getColorScheme() - this._watchMedia() - } else { - this.value = preference - } - document.cookie = serialize(options.cookie.key, this.preference, options.cookie.options) - // Tell others tabs to update the preference - window.localStorage && window.localStorage.setItem(options.cookie.key, preference) - }, - value (newValue, oldValue) { - wColorMode.removeClass(oldValue) - wColorMode.addClass(newValue) - } - }, - created () { - if (this.preference === 'system') { - this._watchMedia() - } - window.localStorage && this._watchCookieChange() - }, - methods: { - _watchMedia () { - if (this._mediaWatcher || !window.matchMedia) return - this._darkWatcher = window.matchMedia('(prefers-color-scheme: dark)') - this._darkWatcher.addListener((e) => { - if (this.preference === 'system') { - this.value = wColorMode.getColorScheme() - } - }) - }, - _watchCookieChange () { - window.addEventListener('storage', (e) => { - if (e.key === options.cookie.key) { - this.preference = e.newValue - } - }) - } - } - }) - - inject('colorMode', colorMode) -} diff --git a/lib/templates/script.js b/lib/templates/script.js deleted file mode 100644 index fa8b5394..00000000 --- a/lib/templates/script.js +++ /dev/null @@ -1,58 +0,0 @@ -// Add dark / light detection that runs before loading Nuxt.js. -(function() { - var getCookie = function (name) { - var nameEQ = name + '='; - var cookies = document.cookie.split(';'); - - for (var i=0; i < cookies.length; i++) { - var cookie = cookies[i]; - while (cookie.charAt(0) === ' ') { - cookie = cookie.substring(1, cookie.length); - } - if (cookie.indexOf(nameEQ) == 0) { - return cookie.substring(nameEQ.length, cookie.length); - } - } - return null; - } - var addClass = function (value) { - var className = value + '-mode' - if (document.documentElement.classList) { - document.documentElement.classList.add(className); - } else { - document.documentElement.className += ' ' + className; - } - } - var removeClass = function (value) { - var className = value + '-mode' - if (document.documentElement.classList) { - document.documentElement.classList.remove(className); - } else { - document.documentElement.className = document.documentElement.className.replace(new RegExp(className, 'g'), '') - } - } - var getColorScheme = function () { - var colorSchemeSupported = window.matchMedia && window.matchMedia('(prefers-color-scheme)').media !== 'not all' - if (colorSchemeSupported && window.matchMedia('(prefers-color-scheme: dark)').matches) { - return 'dark'; - } else if (colorSchemeSupported && window.matchMedia('(prefers-color-scheme: light)').matches) { - return 'light'; - } else { - return '<%= options.fallback %>'; - } - } - var preference = getCookie('<%= options.cookie.key %>') || '<%= options.preference %>'; - var value = preference; - - if (preference === 'system') { - value = getColorScheme() - } - window.__NUXT_COLOR_MODE__ = { - preference, - value, - getColorScheme, - addClass, - removeClass - }; - addClass(value) -})(); diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 00000000..e48f3a43 --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,18 @@ +import { promises as fsp } from 'fs' +import path from 'path' + +export async function addTemplates (templatesDir, name, options) { + const templates = await fsp.readdir(templatesDir) + for (const template of templates) { + const opts = { + src: path.resolve(templatesDir, template), + fileName: path.join(name, template), + options + } + if (template.indexOf('plugin') === 0) { + this.addPlugin(opts) + } else { + this.addTemplate(opts) + } + } +} diff --git a/package.json b/package.json index fc099a91..fb2e02a8 100755 --- a/package.json +++ b/package.json @@ -15,7 +15,9 @@ "main": "lib/module.js", "scripts": { "dev": "nuxt example", + "build": "rollup -c", "lint": "eslint --ext .js,.vue .", + "prepublish": "yarn build", "release": "yarn test && standard-version && git push --follow-tags && npm publish", "test": "yarn lint && jest" }, @@ -38,6 +40,9 @@ "husky": "latest", "jest": "latest", "nuxt-edge": "latest", + "rollup": "^2.6.1", + "rollup-plugin-babel": "^4.4.0", + "rollup-plugin-terser": "^5.3.0", "standard-version": "latest" }, "publishConfig": { diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 00000000..30b4c4b2 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,18 @@ +import { terser } from 'rollup-plugin-terser' +import babel from 'rollup-plugin-babel' + +export default { + input: './lib/script.js', + output: [ + { + file: './lib/script.min.js', + format: 'iife', + plugins: [ + terser() + ] + } + ], + plugins: [ + babel() + ] +} diff --git a/yarn.lock b/yarn.lock index de6f51f5..cb02e590 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== @@ -4555,6 +4555,11 @@ estraverse@^5.0.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.0.0.tgz#ac81750b482c11cca26e4b07e83ed8f75fbcdc22" integrity sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A== +estree-walker@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" + integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -6396,6 +6401,14 @@ jest-watcher@^25.3.0: jest-util "^25.3.0" string-length "^3.1.0" +jest-worker@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" + integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== + dependencies: + merge-stream "^2.0.0" + supports-color "^6.1.0" + jest-worker@^25.1.0, jest-worker@^25.2.6: version "25.2.6" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.2.6.tgz#d1292625326794ce187c38f51109faced3846c58" @@ -9204,6 +9217,39 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rollup-plugin-babel@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-babel/-/rollup-plugin-babel-4.4.0.tgz#d15bd259466a9d1accbdb2fe2fff17c52d030acb" + integrity sha512-Lek/TYp1+7g7I+uMfJnnSJ7YWoD58ajo6Oarhlex7lvUce+RCKRuGRSgztDO3/MF/PuGKmUL5iTHKf208UNszw== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + rollup-pluginutils "^2.8.1" + +rollup-plugin-terser@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-5.3.0.tgz#9c0dd33d5771df9630cd027d6a2559187f65885e" + integrity sha512-XGMJihTIO3eIBsVGq7jiNYOdDMb3pVxuzY0uhOE/FM4x/u9nQgr3+McsjzqBn3QfHIpNSZmFnpoKAwHBEcsT7g== + dependencies: + "@babel/code-frame" "^7.5.5" + jest-worker "^24.9.0" + rollup-pluginutils "^2.8.2" + serialize-javascript "^2.1.2" + terser "^4.6.2" + +rollup-pluginutils@^2.8.1, rollup-pluginutils@^2.8.2: + version "2.8.2" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" + integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== + dependencies: + estree-walker "^0.6.1" + +rollup@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.6.1.tgz#8354e67caa7b8bf24c2488d9e2f64da2be62eebe" + integrity sha512-1RhFDRJeg027YjBO6+JxmVWkEZY0ASztHhoEUEWxOwkh4mjO58TFD6Uo7T7Y3FbmDpRTfKhM5NVxJyimCn0Elg== + optionalDependencies: + fsevents "~2.1.2" + rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -10073,7 +10119,7 @@ terser-webpack-plugin@^2.3.5: terser "^4.4.3" webpack-sources "^1.4.3" -terser@^4.1.2, terser@^4.3.9, terser@^4.4.3, terser@^4.6.11: +terser@^4.1.2, terser@^4.3.9, terser@^4.4.3, terser@^4.6.11, terser@^4.6.2: version "4.6.11" resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.11.tgz#12ff99fdd62a26de2a82f508515407eb6ccd8a9f" integrity sha512-76Ynm7OXUG5xhOpblhytE7X58oeNSmC8xnNhjWVo8CksHit0U0kO4hfNbPrrYwowLWFgM2n9L176VNx2QaHmtA== From bf1e7de9166cd1f6893e9cf1b6e0ce62e05cadac Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Wed, 15 Apr 2020 21:43:15 +0200 Subject: [PATCH 02/10] chore: update deps --- package.json | 9 ++++----- yarn.lock | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index fb2e02a8..5dad7b63 100755 --- a/package.json +++ b/package.json @@ -23,8 +23,7 @@ }, "dependencies": { "defu": "^1.0.0", - "lodash.template": "^4.5.0", - "terser": "^4.6.11" + "lodash.template": "^4.5.0" }, "devDependencies": { "@babel/core": "latest", @@ -40,9 +39,9 @@ "husky": "latest", "jest": "latest", "nuxt-edge": "latest", - "rollup": "^2.6.1", - "rollup-plugin-babel": "^4.4.0", - "rollup-plugin-terser": "^5.3.0", + "rollup": "latest", + "rollup-plugin-babel": "latest", + "rollup-plugin-terser": "latest", "standard-version": "latest" }, "publishConfig": { diff --git a/yarn.lock b/yarn.lock index cb02e590..1eedc6c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9217,7 +9217,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rollup-plugin-babel@^4.4.0: +rollup-plugin-babel@latest: version "4.4.0" resolved "https://registry.yarnpkg.com/rollup-plugin-babel/-/rollup-plugin-babel-4.4.0.tgz#d15bd259466a9d1accbdb2fe2fff17c52d030acb" integrity sha512-Lek/TYp1+7g7I+uMfJnnSJ7YWoD58ajo6Oarhlex7lvUce+RCKRuGRSgztDO3/MF/PuGKmUL5iTHKf208UNszw== @@ -9225,7 +9225,7 @@ rollup-plugin-babel@^4.4.0: "@babel/helper-module-imports" "^7.0.0" rollup-pluginutils "^2.8.1" -rollup-plugin-terser@^5.3.0: +rollup-plugin-terser@latest: version "5.3.0" resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-5.3.0.tgz#9c0dd33d5771df9630cd027d6a2559187f65885e" integrity sha512-XGMJihTIO3eIBsVGq7jiNYOdDMb3pVxuzY0uhOE/FM4x/u9nQgr3+McsjzqBn3QfHIpNSZmFnpoKAwHBEcsT7g== @@ -9243,7 +9243,7 @@ rollup-pluginutils@^2.8.1, rollup-pluginutils@^2.8.2: dependencies: estree-walker "^0.6.1" -rollup@^2.6.1: +rollup@latest: version "2.6.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.6.1.tgz#8354e67caa7b8bf24c2488d9e2f64da2be62eebe" integrity sha512-1RhFDRJeg027YjBO6+JxmVWkEZY0ASztHhoEUEWxOwkh4mjO58TFD6Uo7T7Y3FbmDpRTfKhM5NVxJyimCn0Elg== @@ -10119,7 +10119,7 @@ terser-webpack-plugin@^2.3.5: terser "^4.4.3" webpack-sources "^1.4.3" -terser@^4.1.2, terser@^4.3.9, terser@^4.4.3, terser@^4.6.11, terser@^4.6.2: +terser@^4.1.2, terser@^4.3.9, terser@^4.4.3, terser@^4.6.2: version "4.6.11" resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.11.tgz#12ff99fdd62a26de2a82f508515407eb6ccd8a9f" integrity sha512-76Ynm7OXUG5xhOpblhytE7X58oeNSmC8xnNhjWVo8CksHit0U0kO4hfNbPrrYwowLWFgM2n9L176VNx2QaHmtA== From 70e4297eaa9cb44495ec519d78cea09ba100797a Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Wed, 15 Apr 2020 21:50:30 +0200 Subject: [PATCH 03/10] fix: use global name for script --- lib/script.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/script.js b/lib/script.js index 78194023..e3ffa35f 100644 --- a/lib/script.js +++ b/lib/script.js @@ -5,7 +5,6 @@ const w = window const d = document const de = d.documentElement -const globalName = '__NUXT_COLOR_MODE__' const knownColorSchemes = ['dark', 'light'] const preference = getCookie('<%= options.cookie.key %>') || '<%= options.preference %>' @@ -13,7 +12,7 @@ const value = preference === 'system' ? getColorScheme() : preference addClass(value) -w[globalName] = { +w['<%= options.globalName %>'] = { preference, value, getColorScheme, From f560ba90f25c0d0c97e2847a0a5e69a6da05e3fe Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Wed, 15 Apr 2020 21:52:03 +0200 Subject: [PATCH 04/10] test: collect coverage only from module --- jest.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index f00a7d1c..d0624ccf 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,7 +2,8 @@ module.exports = { testEnvironment: 'node', collectCoverage: true, collectCoverageFrom: [ - 'lib/**/*.js' + 'lib/module.js', + 'lib/utils.js' ], moduleNameMapper: { '^~/(.*)$': '/lib/$1', From ab80dec908b501ed28afe5b2f3a955c899e4bd2d Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Wed, 15 Apr 2020 21:53:10 +0200 Subject: [PATCH 05/10] docs: add hid and globalName --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 1312ed94..7759554f 100755 --- a/README.md +++ b/README.md @@ -96,6 +96,8 @@ You can configure the module by providing the `colorMode` property in your `nuxt colorMode: { preference: 'system', // default value of $colorMode.preference fallback: 'light', // fallback value if not system preference found + hid: 'nuxt-color-mode-script', + globalName: '__NUXT_COLOR_MODE__', cookie: { key: 'nuxt-color-mode', options: { From b15d5f8707da1d63a8dc6aeb57fdd8bdd885e1a4 Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Wed, 15 Apr 2020 21:54:54 +0200 Subject: [PATCH 06/10] docs: mention uknown state --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7759554f..e2df199b 100755 --- a/README.md +++ b/README.md @@ -54,8 +54,9 @@ yarn add --dev @nuxtjs/color-mode ## Usage It injects `$colorMode` helper with: -- `preference`: actual color-mode selected (can be `'system'`), update it to change the user prefered color mode -- `value`: useful to know what color mode has been detected when `$colorMode === 'system'`, you should not update it +- `preference`: Actual color-mode selected (can be `'system'`), update it to change the user prefered color mode +- `value`: Useful to know what color mode has been detected when `$colorMode === 'system'`, you should not update it +- `unknown`: Useful to know if during SSR or Generate, we need to render a placeholder ```vue diff --git a/lib/module.js b/lib/module.js index d2186ae9..0cd0fb2d 100644 --- a/lib/module.js +++ b/lib/module.js @@ -10,6 +10,7 @@ export default async function (moduleOptions) { fallback: 'light', hid: 'nuxt-color-mode-script', globalName: '__NUXT_COLOR_MODE__', + componentName: 'ColorScheme', cookie: { key: 'nuxt-color-mode', options: { diff --git a/lib/templates/color-scheme.js b/lib/templates/color-scheme.js new file mode 100644 index 00000000..d37ced7b --- /dev/null +++ b/lib/templates/color-scheme.js @@ -0,0 +1,17 @@ +export default { + name: '<%= options.componentName %>', + functional: true, + render (createElement, context) { + const tag = context.tag || 'span' + const { $colorMode } = context.parent + + if (!$colorMode.unknown) { + return createElement(tag, context.data, context.children) + } + + return createElement('client-only', { + 'placeholder-tag': tag, + ...context.data + }, context.children) + } +} diff --git a/lib/templates/plugin.client.js b/lib/templates/plugin.client.js index f16da55d..509a2f53 100644 --- a/lib/templates/plugin.client.js +++ b/lib/templates/plugin.client.js @@ -1,5 +1,8 @@ import Vue from 'vue' import { serialize } from 'cookie' +import colorSchemeComponent from './color-scheme' + +Vue.component('<%= options.componentName %>', colorSchemeComponent) const cookieKey = '<%= options.cookie.key %>' const cookieOptions = JSON.parse('<%= JSON.stringify(options.cookie.options) %>') diff --git a/lib/templates/plugin.server.js b/lib/templates/plugin.server.js index 0d2c38bd..13468cf0 100644 --- a/lib/templates/plugin.server.js +++ b/lib/templates/plugin.server.js @@ -1,7 +1,10 @@ +import Vue from 'vue' import { parse } from 'cookie' - +import colorSchemeComponent from './color-scheme' const cookieKey = '<%= options.cookie.key %>' +Vue.component('<%= options.componentName %>', colorSchemeComponent) + export default function (ctx, inject) { let preference = '<%= options.preference %>' From 202055cf29b10be69c7356d4c269599d2c40ded1 Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Wed, 15 Apr 2020 22:13:33 +0200 Subject: [PATCH 08/10] feat: always enable unknown on static mode for server plugin --- lib/templates/plugin.server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/templates/plugin.server.js b/lib/templates/plugin.server.js index 13468cf0..c92e550d 100644 --- a/lib/templates/plugin.server.js +++ b/lib/templates/plugin.server.js @@ -20,7 +20,7 @@ export default function (ctx, inject) { const colorMode = { preference, value: preference, - unknown: preference === 'system' /* TODO: Always true for static */ + unknown: process.static || preference === 'system' } inject('colorMode', colorMode) From dce6fd8a908b6145a65dd33c5041c576d9248741 Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Wed, 15 Apr 2020 22:14:01 +0200 Subject: [PATCH 09/10] feat: enable unknown if req is not available --- lib/templates/plugin.server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/templates/plugin.server.js b/lib/templates/plugin.server.js index c92e550d..7ce90142 100644 --- a/lib/templates/plugin.server.js +++ b/lib/templates/plugin.server.js @@ -20,7 +20,7 @@ export default function (ctx, inject) { const colorMode = { preference, value: preference, - unknown: process.static || preference === 'system' + unknown: process.static || !ctx.req || preference === 'system' } inject('colorMode', colorMode) From 33b61ccdb95752f5308de9aa7ba6a107f87f305f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Wed, 15 Apr 2020 22:50:22 +0200 Subject: [PATCH 10/10] chore: improve caveats section --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 84a5a9bf..be8f24a8 100755 --- a/README.md +++ b/README.md @@ -115,18 +115,18 @@ Notes: ## Caveats -Using SSR if default value is `system` and also With `nuxt generate` and using `$colorMode` in your Vue template, you may expect a flash. This is due to the fact that we cannot know the user preferences when pre-rendering the page, it will directly set the `fallback` value (or `default` value if no set to `'system'`). +If you are doing SSR (`nuxt start` or `nuxt generate`) and if `$colorMode.preference` is set to `'system'`, using `$colorMode` in your Vue template will lead to a flash. This is due to the fact that we cannot know the user preferences when pre-rendering the page since they are detected on client-side. -You have to guard any rendering path which depends on `$colorMode` with `$colorMode.unknown` to render placeholder or use `` component. +You have to guard any rendering path which depends on `$colorMode` with `$colorMode.unknown` to render a placeholder or directory use our `` component. ***Example:** ```vue ```