Skip to content

Commit

Permalink
Feature updates and fixes
Browse files Browse the repository at this point in the history
- Finished Vue 3 implementation
- Fixed all feature tests
- Added missing `HotModuleReplacementPlugin` plugin instance
- Updated all npm dependencies
  • Loading branch information
cshawaus committed Oct 27, 2020
1 parent 5928b0d commit 7c8a5bf
Show file tree
Hide file tree
Showing 16 changed files with 696 additions and 408 deletions.
28 changes: 14 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,17 @@
"file-loader": "^6.1.1",
"imports-loader": "^1.2.0",
"lodash": "^4.17.20",
"mini-css-extract-plugin": "^1.1.0",
"mini-css-extract-plugin": "^1.2.0",
"postcss": "^7.0.35",
"postcss-loader": "^4.0.4",
"rimraf": "^3.0.2",
"sass": "^1.27.0",
"sass-loader": "^10.0.3",
"sass-loader": "^10.0.4",
"style-loader": "^2.0.0",
"stylelint": "^13.7.2",
"stylelint-webpack-plugin": "^2.1.1",
"terser-webpack-plugin": "^5.0.0",
"webpack": "^5.1.3",
"terser-webpack-plugin": "^5.0.1",
"webpack": "^5.2.1",
"webpack-bundle-analyzer": "^3.9.0",
"webpack-config-utils": "^2.3.1",
"webpack-dev-server": "^3.11.0",
Expand All @@ -127,38 +127,38 @@
"@types/figlet": "^1.2.0",
"@types/jest": "^26.0.15",
"@types/lodash": "^4.14.162",
"@types/mini-css-extract-plugin": "^1.0.0",
"@types/mini-css-extract-plugin": "^1.2.0",
"@types/mock-fs": "^4.13.0",
"@types/node": "^14.14.0",
"@types/node": "^14.14.5",
"@types/pify": "^3.0.2",
"@types/rimraf": "^3.0.0",
"@types/sass": "^1.16.0",
"@types/terser-webpack-plugin": "^5.0.0",
"@types/webpack-bundle-analyzer": "^3.8.0",
"@types/webpack-bundle-analyzer": "^3.9.0",
"@types/webpack-config-utils": "^2.3.1",
"@types/webpack-dev-server": "^3.11.0",
"@types/webpack-env": "^1.15.3",
"@types/webpack-merge": "^4.1.5",
"@types/xml2js": "^0.4.5",
"@types/yargs": "^15.0.9",
"@typescript-eslint/eslint-plugin": "^4.5.0",
"@typescript-eslint/parser": "^4.5.0",
"@typescript-eslint/eslint-plugin": "^4.6.0",
"@typescript-eslint/parser": "^4.6.0",
"cross-env": "^7.0.2",
"eslint": "^7.11.0",
"eslint": "^7.12.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.1.0",
"husky": "^4.3.0",
"jest": "^26.6.0",
"jest": "^26.6.1",
"jest-fetch-mock": "^3.0.3",
"jest-mock-console": "^1.0.1",
"jest-mock-process": "^1.4.0",
"memfs": "^3.2.0",
"mock-fs": "^4.13.0",
"ts-jest": "^26.4.1",
"ts-jest": "^26.4.3",
"ts-node": "^9.0.0",
"ttypescript": "^1.5.12",
"typescript": "^4.0.3",
"typescript-transform-paths": "^2.0.1"
"typescript": "^4.0.5",
"typescript-transform-paths": "^2.0.2"
},
"peerDependencies": {
"@babel/core": "^7.11.0",
Expand Down
3 changes: 2 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const configKeys = Object.values(ConfigurationType)

const webpackConfigurables: WebpackConfigurables = {
assetFilters : ['fontawesome.*'],
moduleRules : [],
resolveExtensions : ['.js'],
}

Expand Down Expand Up @@ -240,7 +241,7 @@ export function setConfigurable<T extends keyof WebpackConfigurables, R extends
let newValue = _get(webpackConfigurables, key)

if (Array.isArray(value)) {
newValue = [...newValue, ...value]
newValue = [...newValue, ...value] as R
} else {
newValue = value
}
Expand Down
2 changes: 1 addition & 1 deletion src/features/feature.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DependencyType } from '@/types/enums'
import Feature from '@/features/feature'

describe('base feature', () => {
let instance: Feature
let instance: Feature<any, any>

beforeEach(() => {
// @ts-expect-error several other properties are missing for 'env'
Expand Down
2 changes: 1 addition & 1 deletion src/features/feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default class Feature<F extends Features, O = FeatureOptions[F]> extends
protected env!: FeatureEnvironment
protected options!: Required<O>

public constructor(env: FeatureEnvironment, options: O) {
public constructor(env: FeatureEnvironment, options?: O) {
super()

this.env = env
Expand Down
20 changes: 15 additions & 5 deletions src/features/process.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ describe('process features', () => {

// @ts-expect-error part of the configuration is omitted on purpose as it isn't required
expect(processFeatures({
features: ['typescript'],
features: {
typescript: true,
},

...featureConfig,
})).toBeNull()
Expand All @@ -53,7 +55,9 @@ describe('process features', () => {

// @ts-expect-error several other properties are missing for 'env'
expect(processFeatures({
features: ['typescript'],
features: {
typescript: true,
},

...featureConfig,
})).toBeNull()
Expand All @@ -69,10 +73,12 @@ describe('process features', () => {

// @ts-expect-error several other properties are missing for 'env'
expect(processFeatures({
features: ['vue'],
features: {
vue: true,
},

...featureConfig,
})).toHaveProperty('resolve.alias.vue$', 'vue/dist/vue.min.js')
})).toHaveProperty('resolve.alias.vue$', 'vue/dist/vue.esm.js')
})

test('consecutive features correct set restart status', () => {
Expand All @@ -82,7 +88,11 @@ describe('process features', () => {

// @ts-expect-error several other properties are missing for 'env'
expect(processFeatures({
features: ['bootstrap', 'vue', 'typescript'],
features: {
bootstrap : true,
typescript : true,
vue : true,
},

...featureConfig,
})).toBeNull()
Expand Down
24 changes: 13 additions & 11 deletions src/features/vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default class Vue extends Feature<Features.vue> {
],

[DependencyType.NON_DEV]: [
this.options.version === 3 ? 'vue@^3.0.0' : 'vue@^2.6.11',
this.options.version === 3 ? 'vue@^3.0.0' : 'vue@^2.6.12',
],
}

Expand Down Expand Up @@ -99,30 +99,32 @@ export default class Vue extends Feature<Features.vue> {
},
},
},

plugins: removeEmpty([
this.options.version === 3 ? new webpack.DefinePlugin({
__VUE_OPTIONS_API__ : JSON.stringify(this.options.useOptionsAPI),
__VUE_PROD_DEVTOOLS__ : JSON.stringify(this.options.enableDevTools ?? this.env.mode === 'development'),
}) : undefined,
]),
}
}

public plugins(): webpack.WebpackPluginInstance[] {
const { VueLoaderPlugin } = require(resolveDependency('vue-loader'))

return [
return removeEmpty([
new VueLoaderPlugin() as webpack.WebpackPluginInstance,
]

this.options.version === 3 ? new webpack.DefinePlugin({
__VUE_OPTIONS_API__ : this.options.useOptionsAPI,
__VUE_PROD_DEVTOOLS__ : this.options.enableDevTools ?? this.env.mode === 'development',
}) : undefined,
])
}

public rules(): webpack.RuleSetRule[] {
return [
// Set 'vue-loader' in a configurable due to a bug which causes file resolution to fail
setConfigurable('moduleRules', [
{
loader : 'vue-loader',
test : /\.vue$/,
},
])

return [
{
test: /\.(c|sc)ss$/,

Expand Down
47 changes: 34 additions & 13 deletions src/features/vue.spec.ts → src/features/vue2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import Vue from '@/features/vue'

jest.mock('../support/dependencies')

describe('vue feature', () => {
let instance: Vue
let instance: Vue

describe('default config', () => {
beforeEach(() => {
instance = new Vue({
mode: 'development',
Expand All @@ -25,18 +25,17 @@ describe('vue feature', () => {
const dependencies = instance.getFeatureDependencies()

expect(dependencies).toHaveProperty(DependencyType.DEV, [
'@vue/cli-plugin-babel@^4.3.1',
'@vue/cli-plugin-eslint@^4.3.1',
'@vue/compiler-sfc@^3.0.0',
'@vue/eslint-config-typescript@^5.0.2',
'@vue/cli-plugin-babel@^4.5.7',
'@vue/cli-plugin-eslint@^4.5.7',
'@vue/eslint-config-typescript@^7.0.0',
'babel-preset-vue@^2.0.2',
'vue-loader@^v16.0.0-beta.8',
'vue-style-loader@^4.1.2',
'vue-template-compiler@^2.6.11',
'vue-loader@^15.9.1',
'vue-template-compiler',
])

expect(dependencies).toHaveProperty(DependencyType.NON_DEV, [
'vue@^2.6.11',
'vue@^2.6.12',
'vue-property-decorator@^8.4.2',
])
})
Expand Down Expand Up @@ -69,9 +68,31 @@ describe('vue feature', () => {
test('should return the correct webpack rules', () => {
const rules = instance.rules()

expect(rules).toHaveProperty([0, 'loader'], 'vue-loader')
expect(rules).toHaveProperty([1, 'use', 0, 'loader'], 'vue-style-loader')
expect(rules).toHaveProperty([1, 'use', 2, 'loader'], 'postcss-loader')
expect(rules).toHaveProperty([1, 'use', 3, 'options', 'sassOptions', 'outputStyle'], 'expanded')
expect(getConfigurable('moduleRules')[0].loader).toStrictEqual('vue-loader')

expect(rules).toHaveProperty([0, 'use', 0, 'loader'], 'vue-style-loader')
expect(rules).toHaveProperty([0, 'use', 2, 'loader'], 'postcss-loader')
expect(rules).toHaveProperty([0, 'use', 3, 'options', 'sassOptions', 'outputStyle'], 'expanded')
})
})

describe('runtime only', () => {
beforeEach(() => {
instance = new Vue({
mode: 'production',

paths: {
// @ts-expect-error only part of the project object is mocked
project: {
src: 'mocked/path',
},
},
}, {
runtimeOnly: true,
})
})

test('should return the ESM Vue.js distribution file', () => {
expect(instance.aliases()).toHaveProperty('vue$', 'vue/dist/vue.runtime.esm.js')
})
})
104 changes: 104 additions & 0 deletions src/features/vue3.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import webpack from 'webpack'

import { getConfigurable } from '@/config'
import { DependencyType } from '@/types/enums'

import Vue from '@/features/vue'

jest.mock('../support/dependencies')

let instance: Vue

describe('default config', () => {
beforeEach(() => {
instance = new Vue({
mode: 'development',

paths: {
// @ts-expect-error only part of the project object is mocked
project: {
src: 'mocked/path',
},
},
}, {
version: 3,
})
})

test('should have the correct NPM dependencies', () => {
const dependencies = instance.getFeatureDependencies()

expect(dependencies).toHaveProperty(DependencyType.DEV, [
'@vue/cli-plugin-babel@^4.5.7',
'@vue/cli-plugin-eslint@^4.5.7',
'@vue/eslint-config-typescript@^7.0.0',
'babel-preset-vue@^2.0.2',
'vue-style-loader@^4.1.2',
'vue-loader@^v16.0.0-beta.8',
'@vue/compiler-sfc',
])

expect(dependencies).toHaveProperty(DependencyType.NON_DEV, [
'vue@^3.0.0',
])
})

test('should return the ESM Vue.js distribution file', () => {
expect(instance.aliases()).toHaveProperty('vue$', 'vue/dist/vue.esm-bundler.js')
})

test('should set correct arbitrary webpack configuration', () => {
expect(instance.arbitraryUpdates())
.toHaveProperty('optimization.splitChunks.cacheGroups.vue.name', 'vue')

expect(getConfigurable('assetFilters')).toContainEqual('vue')
})

test('should set correct webpack plugins configuration', () => {
const MockedVueLoaderPlugin = {
VueLoaderPlugin: jest.fn(),
}

jest.mock('vue-loader', () => MockedVueLoaderPlugin, { virtual: true })

const plugins = instance.plugins()

expect(MockedVueLoaderPlugin.VueLoaderPlugin).toHaveBeenCalledTimes(1)

expect(plugins[0]).toBeInstanceOf(MockedVueLoaderPlugin.VueLoaderPlugin)

expect(plugins[1]).toBeInstanceOf(webpack.DefinePlugin)
})

test('should return the correct webpack rules', () => {
const rules = instance.rules()

expect(getConfigurable('moduleRules')[0].loader).toStrictEqual('vue-loader')

expect(rules).toHaveProperty([0, 'use', 0, 'loader'], 'vue-style-loader')
expect(rules).toHaveProperty([0, 'use', 2, 'loader'], 'postcss-loader')
expect(rules).toHaveProperty([0, 'use', 3, 'options', 'sassOptions', 'outputStyle'], 'expanded')
})
})

describe('runtime only', () => {
beforeEach(() => {
instance = new Vue({
mode: 'production',

paths: {
// @ts-expect-error only part of the project object is mocked
project: {
src: 'mocked/path',
},
},
}, {
runtimeOnly : true,
version : 3,
})
})

test('should return the ESM Vue.js distribution file', () => {
expect(instance.aliases()).toHaveProperty('vue$', 'vue/dist/vue.runtime.esm-bundler.js')
})
})
7 changes: 7 additions & 0 deletions src/plugins/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,5 +120,12 @@ export default (paths: RuntimePaths): webpack.WebpackPluginInstance[] => {
openAnalyzer: false,
})),

/**
* Enable HMR when the `hmr` flag is set to `true`.
*
* @see https://webpack.js.org/plugins/hot-module-replacement-plugin/
*/
getIfUtilsInstance().ifHmr(new webpack.HotModuleReplacementPlugin()),

]) as webpack.WebpackPluginInstance[]
}
Loading

0 comments on commit 7c8a5bf

Please sign in to comment.