From cde03f6c4d5bb6cf0c50d3fe40b36c0400a9c3c4 Mon Sep 17 00:00:00 2001 From: Kevin Marrec Date: Sat, 28 Sep 2019 21:19:21 +0200 Subject: [PATCH 01/33] refactor: begin v2 work --- src/automaticImports.ts | 22 ++++++++ src/build.ts | 50 ------------------ src/font.ts | 9 ++-- src/index.ts | 13 +++-- src/options.ts | 27 +++------- src/plugin.ts | 26 ++++++++++ src/sass.ts | 12 +++-- test/module.test.ts | 110 ++++++++++++++++++++-------------------- 8 files changed, 131 insertions(+), 138 deletions(-) create mode 100644 src/automaticImports.ts delete mode 100644 src/build.ts create mode 100644 src/plugin.ts diff --git a/src/automaticImports.ts b/src/automaticImports.ts new file mode 100644 index 00000000..bf2152b4 --- /dev/null +++ b/src/automaticImports.ts @@ -0,0 +1,22 @@ +import { ModuleThis } from '@nuxt/types/config/module' +import { SFCDescriptor } from 'vue-template-compiler' +import { Options } from './options' + +// https://github.com/vuetifyjs/vuetify-loader#automatic-imports + +export interface VuetifyLoaderOptions { + match?(originalTag: string, context: { + kebabTag: string, + camelTag: string, + path: string, + component: SFCDescriptor + }): Array<[string, string]> +} + +export default function setupAutomaticImports (this: ModuleThis, options: Options['automaticImports']) { + const VuetifyLoaderPlugin = this.nuxt.resolver.requireModule('vuetify-loader/lib/plugin') + + this.extendBuild((config) => { + config.plugins!.push(new VuetifyLoaderPlugin(typeof options === 'object' ? options : {})) + }) +} diff --git a/src/build.ts b/src/build.ts deleted file mode 100644 index 98fe583d..00000000 --- a/src/build.ts +++ /dev/null @@ -1,50 +0,0 @@ -import path from 'path' -import fs from 'fs' -import { ModuleThis } from '@nuxt/types/config/module' -import { Options } from './options' - -export default function setupBuild (this: ModuleThis, options: Options) { - if (!options.treeShake) { - this.options.css!.push('vuetify/dist/vuetify.css') - } - - // Enable tree-shaking with VuetifyLoader (https://github.com/vuetifyjs/vuetify-loader) - if (options.treeShake) { - const VuetifyLoaderPlugin = this.nuxt.resolver.requireModule('vuetify-loader/lib/plugin') - - this.options.build!.transpile!.push('vuetify/lib') - - this.extendBuild((config) => { - config.plugins!.push(new VuetifyLoaderPlugin(typeof options.treeShake === 'object' ? options.treeShake.loaderOptions : {})) - }) - } - - // Remove module options - const vuetifyOptions = { ...options } - delete vuetifyOptions.customVariables - delete vuetifyOptions.defaultAssets - delete vuetifyOptions.optionsPath - delete vuetifyOptions.treeShake - - let optionsPath: string | null = this.nuxt.resolver.resolveAlias(options.optionsPath || - path.join(this.options.dir!.app || 'app', 'vuetify', 'options.js')) - - optionsPath = fs.existsSync(optionsPath!) ? optionsPath : null - - // Register options template - this.addTemplate({ - fileName: `vuetify/options.${optionsPath && optionsPath.endsWith('ts') ? 'ts' : 'js'}`, - src: optionsPath || path.resolve(__dirname, '../templates', 'options.js'), - options: vuetifyOptions - }) - - // Register plugin - this.addPlugin({ - fileName: 'vuetify/plugin.js', - src: path.resolve(__dirname, '../templates', 'plugin.js'), - options: { - defaultIconPreset: options.defaultAssets && options.defaultAssets.icons, - treeShake: options.treeShake - } - }) -} diff --git a/src/font.ts b/src/font.ts index 598bb8c5..b41e6483 100644 --- a/src/font.ts +++ b/src/font.ts @@ -1,4 +1,5 @@ import { ModuleThis } from '@nuxt/types/config/module' +import { prependData as sassPrependData } from './sass' export interface FontOptions { family?: string @@ -21,12 +22,8 @@ export default function setupFont (this: ModuleThis, options: FontOptions) { } // Add font-family custom variable (only if not Roboto, cause already default in Vuetify styles) - if (options.family !== 'Roboto') { - this.options.build!.loaders.sass.prependData = [`$body-font-family: '${options.family}', sans-serif`, this.options.build!.loaders.sass.prependData].join('\n') - } + options.family !== 'Roboto' && sassPrependData.call(this, `$body-font-family: '${options.family}', sans-serif`) // Add font-size custom variable - if (options.size) { - this.options.build!.loaders.sass.prependData = [`$font-size-root: ${options.size}px`, this.options.build!.loaders.sass.prependData].join('\n') - } + options.size && sassPrependData.call(this, `$font-size-root: ${options.size}px`) } diff --git a/src/index.ts b/src/index.ts index 95943957..27517c19 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,11 @@ import { Module } from '@nuxt/types' import { Framework } from 'vuetify' -import initOptions, { Options, TreeShakeOptions, VuetifyLoaderOptions } from './options' -import setupBuild from './build' +import initOptions, { Options } from './options' +import setupAutomaticImports, { VuetifyLoaderOptions } from './automaticImports' import setupFont from './font' import setupIcons from './icons' +import setupPlugin from './plugin' import setupSass from './sass' declare module '@nuxt/types' { @@ -21,19 +22,21 @@ const vuetifyModule: Module = function (moduleOptions) { this.nuxt.hook('build:before', () => { const options = initOptions.call(this, moduleOptions) + setupSass.call(this, options.customVariables) + if (typeof options.defaultAssets === 'object') { options.defaultAssets.font && setupFont.call(this, options.defaultAssets.font) options.defaultAssets.icons && setupIcons.call(this, options.defaultAssets.icons) } - setupSass.call(this, options.customVariables) - setupBuild.call(this, options) + options.automaticImports && setupAutomaticImports.call(this, options.automaticImports) + + setupPlugin.call(this, options) }) } export { Options, - TreeShakeOptions, VuetifyLoaderOptions } diff --git a/src/options.ts b/src/options.ts index 81f0798e..1fc46780 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,48 +1,37 @@ import merge from 'deepmerge' -import { SFCDescriptor } from 'vue-template-compiler' import { VuetifyPreset } from 'vuetify/types/presets' import { ModuleThis } from '@nuxt/types/config/module' +import { VuetifyLoaderOptions } from './automaticImports' import { FontOptions } from './font' import { IconPreset } from './icons' -export interface TreeShakeOptions { +export interface GlobalImports { components?: string[] directives?: string[] - loaderOptions?: VuetifyLoaderOptions transitions?: string[] } -export interface VuetifyLoaderOptions { - match?(originalTag: string, context: { - kebabTag: string, - camelTag: string, - path: string, - component: SFCDescriptor - }): Array<[string, string]> -} - -export interface Options extends Partial { +export interface Options { + automaticImports?: boolean | VuetifyLoaderOptions customVariables?: string[] defaultAssets?: { font?: FontOptions, icons?: IconPreset | false } | false - optionsPath?: string - treeShake?: boolean | TreeShakeOptions + frameworkOptions?: string | Partial + globalImports?: GlobalImports } export const defaults = { - customVariables: [], + automaticImports: true, defaultAssets: { font: { family: 'Roboto' }, icons: 'mdi' as IconPreset - }, - optionsPath: undefined, - treeShake: process.env.NODE_ENV === 'production' + } } export default function initOptions (this: ModuleThis, moduleOptions?: Options): Required { diff --git a/src/plugin.ts b/src/plugin.ts new file mode 100644 index 00000000..1973acdd --- /dev/null +++ b/src/plugin.ts @@ -0,0 +1,26 @@ +import path from 'path' +import { ModuleThis } from '@nuxt/types/config/module' +import { Options } from './options' + +export default function setupPlugin (this: ModuleThis, options: Options) { + this.options.build!.transpile!.push('vuetify/lib') + + const optionsPath = typeof options.frameworkOptions === 'string' && this.nuxt.resolver.resolveAlias(options.frameworkOptions) + + // Register options template + this.addTemplate({ + fileName: `vuetify/options.${optionsPath && optionsPath.endsWith('ts') ? 'ts' : 'js'}`, + src: optionsPath || path.resolve(__dirname, '../templates', 'options.js'), + options: options.frameworkOptions || {} + }) + + // Register plugin + this.addPlugin({ + fileName: 'vuetify/plugin.js', + src: path.resolve(__dirname, '../templates', 'plugin.js'), + options: { + defaultIconPreset: options.defaultAssets && options.defaultAssets.icons, + treeShake: true + } + }) +} diff --git a/src/sass.ts b/src/sass.ts index 9989fd19..a8fd21f3 100644 --- a/src/sass.ts +++ b/src/sass.ts @@ -2,6 +2,13 @@ import { ModuleThis } from '@nuxt/types/config/module' import dartSass from 'sass' import { Options } from './options' +export function prependData (this: ModuleThis, ...datas: string[]) { + const { sass, scss } = this.options.build!.loaders + + sass.prependData = [sass.prependData, ...datas].join('\n') + scss.prependData = [scss.prependData, ...datas.map(d => d + ';')].join('\n') +} + export default function setupSass (this: ModuleThis, customVariables: Options['customVariables']) { const { sass, scss } = this.options.build!.loaders @@ -18,9 +25,6 @@ export default function setupSass (this: ModuleThis, customVariables: Options['c // Custom variables if (customVariables && customVariables.length > 0) { - const sassImports = customVariables.map(path => `@import '${path}'`).join('\n') - sass.prependData = [sass.prependData, sassImports].join('\n') - const scssImports = customVariables.map(path => `@import '${path}';`).join('\n') - scss.prependData = [scss.prependData, scssImports].join('\n') + prependData.call(this, ...customVariables.map(path => `@import '${path}'`)) } } diff --git a/test/module.test.ts b/test/module.test.ts index 264a94eb..a8dbada6 100644 --- a/test/module.test.ts +++ b/test/module.test.ts @@ -3,10 +3,11 @@ import dartSass from 'sass' import VuetifyLoaderPlugin from 'vuetify-loader/lib/plugin' import _vuetifyModule from '../src' -import _initOptions, { defaults as defaultOptions, Options, VuetifyLoaderOptions } from '../src/options' -import _setupBuild from '../src/build' +import _initOptions, { defaults as defaultOptions, Options } from '../src/options' +import _setupAutomaticImports, { VuetifyLoaderOptions } from '../src/automaticImports' import _setupFont, { FontOptions } from '../src/font' import _setupIcons, { IconPreset } from '../src/icons' +import _setupPlugin from '../src/plugin' import _setupSass from '../src/sass' jest.mock('vuetify-loader/lib/plugin') @@ -18,12 +19,13 @@ const vuetifyModule = async (options?: Options) => { await nuxt.callHook('build:before') } const initOptions = (options?: Options): Required => _initOptions.call(nuxt.moduleContainer, options) -const setupBuild = (options?: Options) => { - _setupBuild.call(nuxt.moduleContainer, options) +const setupAutomaticImports = (options?: Options['automaticImports']) => { + _setupAutomaticImports.call(nuxt.moduleContainer, options) nuxt.options.build.extend && nuxt.options.build.extend({ plugins: [] }) } const setupFont = (options?: FontOptions) => _setupFont.call(nuxt.moduleContainer, options) const setupIcons = (preset?: IconPreset) => _setupIcons.call(nuxt.moduleContainer, preset) +const setupPlugin = (options?: Options) => _setupPlugin.call(nuxt.moduleContainer, options) const setupSass = (customVariables?: Options['customVariables']) => _setupSass.call(nuxt.moduleContainer, customVariables) beforeEach(async () => { @@ -39,6 +41,31 @@ describe('initOptions', () => { }) }) +describe('setupSass', () => { + test('default', () => { + delete nuxt.options.build.loaders.sass.sassOptions + + setupSass() + + const { sass, scss } = nuxt.options.build.loaders + + expect(sass.implementation).toEqual(dartSass) + expect(scss.implementation).toEqual(dartSass) + + expect(sass.indentedSyntax).toBeUndefined() + expect(sass.sassOptions.indentedSyntax).toBe(true) + }) + + test('customVariables', () => { + setupSass(['/path/to/variables.scss']) + + const { sass, scss } = nuxt.options.build.loaders + + expect(sass.prependData).toContain("@import '/path/to/variables.scss'") + expect(scss.prependData).toContain("@import '/path/to/variables.scss';") + }) +}) + describe('setupFont', () => { test('default', () => { setupFont(defaultOptions.defaultAssets.font) @@ -64,10 +91,13 @@ describe('setupFont', () => { } }) - const { prependData } = nuxt.options.build.loaders.sass + const { sass, scss } = nuxt.options.build.loaders + + expect(sass.prependData).toContain("$body-font-family: 'Montserrat', sans-serif") + expect(scss.prependData).toContain("$body-font-family: 'Montserrat', sans-serif;") - expect(prependData).toContain("$body-font-family: 'Montserrat', sans-serif") - expect(prependData).toContain('$font-size-root: 20px') + expect(sass.prependData).toContain('$font-size-root: 20px') + expect(scss.prependData).toContain('$font-size-root: 20px;') }) }) @@ -83,73 +113,45 @@ describe('setupIcons', () => { }) }) -describe('setupSass', () => { +describe('setupAutomaticImports', () => { test('default', () => { - delete nuxt.options.build.loaders.sass.sassOptions - - setupSass() - - const { sass, scss } = nuxt.options.build.loaders + setupAutomaticImports(true) - expect(sass.implementation).toEqual(dartSass) - expect(scss.implementation).toEqual(dartSass) - - expect(sass.indentedSyntax).toBeUndefined() - expect(sass.sassOptions.indentedSyntax).toBe(true) + expect(VuetifyLoaderPlugin).toHaveBeenCalled() }) - test('customVariables', () => { - setupSass(['/path/to/variables.scss']) + test('with options', () => { + const options: VuetifyLoaderOptions = { + match () { + return [] + } + } + + setupAutomaticImports(options) - expect(nuxt.options.build.loaders.sass.prependData).toContain("@import '/path/to/variables.scss'") - expect(nuxt.options.build.loaders.scss.prependData).toContain("@import '/path/to/variables.scss';") + expect(VuetifyLoaderPlugin).toHaveBeenCalledWith(options) }) }) -describe('setupBuild', () => { +describe('setupPlugin', () => { test('default', () => { nuxt.options.dir.app = '' - setupBuild(defaultOptions) + setupPlugin(defaultOptions) + + expect(nuxt.options.build.transpile).toContain('vuetify/lib') - expect(nuxt.options.css).toContain('vuetify/dist/vuetify.css') expect(nuxt.options.build.templates.map(t => t.dst)).toEqual(['vuetify/options.js', 'vuetify/plugin.js']) }) - test('optionsPath', () => { - setupBuild({ + test('frameworkOptions', () => { + setupPlugin({ ...defaultOptions, - optionsPath: 'test/fixture/vuetify.options.ts' + frameworkOptions: 'test/fixture/vuetify.options.ts' }) expect(nuxt.options.build.templates.map(t => t.dst)).toContain('vuetify/options.ts') }) - - test('treeShake', () => { - setupBuild({ - treeShake: true - }) - - expect(nuxt.options.build.transpile).toContain('vuetify/lib') - expect(VuetifyLoaderPlugin).toHaveBeenCalled() - }) - - test('treeShake with loaderOptions', () => { - const loaderOptions: VuetifyLoaderOptions = { - match () { - return [] - } - } - - setupBuild({ - treeShake: { - loaderOptions - } - }) - - expect(nuxt.options.build.transpile).toContain('vuetify/lib') - expect(VuetifyLoaderPlugin).toHaveBeenCalledWith(loaderOptions) - }) }) describe('module', () => { From 45a1a7e087062bd9e91909ccc21527f4f0b6b49a Mon Sep 17 00:00:00 2001 From: Kevin Marrec Date: Sat, 28 Sep 2019 21:23:39 +0200 Subject: [PATCH 02/33] fix: fixture module configuration --- test/fixture/nuxt.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixture/nuxt.config.ts b/test/fixture/nuxt.config.ts index eb184025..df3daece 100644 --- a/test/fixture/nuxt.config.ts +++ b/test/fixture/nuxt.config.ts @@ -12,7 +12,7 @@ const config: Partial = { vuetify: { customVariables: ['~/assets/variables.scss'], - optionsPath: './vuetify.options.ts' + frameworkOptions: './vuetify.options.ts' } } From 33962d4be6439a24476f30844751205d5385b960 Mon Sep 17 00:00:00 2001 From: Patrick Sullivan Date: Thu, 10 Oct 2019 08:44:27 -0700 Subject: [PATCH 03/33] fix(deps): fibers is optional dependency (#179) --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index d9e8ed54..b60ed454 100644 --- a/package.json +++ b/package.json @@ -30,12 +30,14 @@ ], "dependencies": { "deepmerge": "^4.0.0", - "fibers": "^4.0.1", "sass": "^1.23.0", "sass-loader": "^8.0.0", "vuetify": "^2.1.0", "vuetify-loader": "^1.3.0" }, + "optionalDependencies": { + "fibers": "^4.0.1" + }, "devDependencies": { "@commitlint/cli": "latest", "@commitlint/config-conventional": "latest", From 9f60881fe4ddfd21fce026487e567842247bae1e Mon Sep 17 00:00:00 2001 From: Kevin Marrec Date: Fri, 18 Oct 2019 14:04:57 +0200 Subject: [PATCH 04/33] chore: update fixture grid spec --- test/fixture/layouts/default.vue | 4 ++-- test/fixture/pages/dynamic-component.vue | 11 ----------- test/fixture/pages/index.vue | 8 ++++---- 3 files changed, 6 insertions(+), 17 deletions(-) delete mode 100644 test/fixture/pages/dynamic-component.vue diff --git a/test/fixture/layouts/default.vue b/test/fixture/layouts/default.vue index 8da4d895..416bccb7 100644 --- a/test/fixture/layouts/default.vue +++ b/test/fixture/layouts/default.vue @@ -54,7 +54,7 @@ Youtube - + - + diff --git a/test/fixture/pages/dynamic-component.vue b/test/fixture/pages/dynamic-component.vue deleted file mode 100644 index 282ecfcc..00000000 --- a/test/fixture/pages/dynamic-component.vue +++ /dev/null @@ -1,11 +0,0 @@ - diff --git a/test/fixture/pages/index.vue b/test/fixture/pages/index.vue index 40a2c15d..0ca7f486 100644 --- a/test/fixture/pages/index.vue +++ b/test/fixture/pages/index.vue @@ -1,7 +1,7 @@