Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
dist
36 changes: 36 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module.exports = {
env: {
browser: true,
es2021: true,
node: true
},
extends: [
'standard' // javascript standard 語法規範。
],
ignorePatterns: ['node_modules/**', 'dist/**'], // 排除檢查
plugins: ['jest'],
overrides: [
{
env: {
node: true,
jest: true
},
files: ['.eslintrc.{js,cjs}', '**/__tests__/*.{j,t}s?(x)', '**/*.spec.{j,t}s?(x)', 'tests/**/*.js'],
plugins: ['jest'],
parserOptions: {
sourceType: 'module'
}
}
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module'
},
rules: {
indent: ['error', 2],
quotes: ['error', 'single'],
semi: ['error', 'never'],
'linebreak-style': ['error', 'unix'],
'comma-dangle': ['error', 'never']
}
}
25 changes: 25 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
vscode/extensions.json
vscode/settings.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
26 changes: 16 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,23 @@ export const ILoading = {
}


//IButton
export const IButton = VueInheritance
.extend(IControl)
.extend(ITheme)
.extend({
methods: {
onClick(e) {
this.$emit('click', e)
}
// IButton
export const IButton = {
extends: VueInheritance.implement(IControl).implement(ITheme)
props: {
size: {
type: String,
default: 'lg'
}
})
},

methods: {
}
}




```

**Implement**
Expand Down
3 changes: 3 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: [['@babel/preset-env', {targets: {node: 'current'}}]],
};
9 changes: 9 additions & 0 deletions jest.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"verbose": true,
"testEnvironment": "node",
"transform": {
"^.+\\.js$": "babel-jest"
}
}


17 changes: 15 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "vue-inheritance",
"version": "1.0.1-beta.4",
"version": "1.0.1-beta.5",
"description": "vue inheritance",
"main": "src/index.js",
"types": "src/index.d.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "jest"
},
"repository": {
"type": "git",
Expand All @@ -28,5 +28,18 @@
"homepage": "https://github.com/en96321/vue-Inheritance#readme",
"dependencies": {
"ramda": "0.29.1"
},
"devDependencies": {
"@babel/core": "^7.23.7",
"@babel/preset-env": "^7.23.8",
"babel-jest": "^29.7.0",
"eslint": "^8.56.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-jest": "^27.6.3",
"eslint-plugin-n": "^16.0.0",
"eslint-plugin-promise": "^6.0.0",
"jest": "^29.7.0",
"vue": "^3.4.15"
}
}
157 changes: 129 additions & 28 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,135 @@
import { mergeRight, clone } from 'ramda'

export class VueInheritance {
constructor ({
props,
methods,
computed
}) {
this.props = clone(props)
this.methods = clone(methods)
this.computed = clone(computed)
import { clone, isNil, isNotNil, has, isEmpty } from 'ramda'

/**
* Vue3Component
* @class
* @private
* @author pedro.yang、 ocean.tsai
* @description
*/
class Vue3Component {
/**
* InvalidInterfaceKeys
* @static
* @constant
* @private
* @type
* @description interfaceDefine will be checked, if it conforms to the interface, it return true; otherwise, it returns false.
*/
static InvalidInterfaceKeys = Object.freeze([
'watch',
'beforeCreate', 'created',
'beforeMount', 'mounted', 'beforeUpdate', 'updated', 'beforeUnmount', 'unmounted',
'errorCaptured', 'renderTracked', 'activated', 'deactivated', 'serverPrefetch'
])

/**
* validateInterface
* @static
* @method
* @protected
* @param {VueInterface} interfaceDefine the param will be checked.
* @description interfaceDefine will be checked, if it conforms to the interface, it return true; otherwise, it returns false.
*/
static validateInterface (interfaceDefine) {
// hasIn 是包含 prototype has 不會含 prototype
return Vue3Component.InvalidInterfaceKeys.every((key) => !has(key, interfaceDefine))
}

/**
* typeCheck
* @static
* @method
* @protected
* @param {VueInterface} interfaceDefine the param will be checked.
* @description interfaceDefine will be checked, if it conforms to the interface, it return true; otherwise, it returns false.
*/
static typeCheck (interfaceDefine) {
if (isNil(interfaceDefine)) {
throw new Error('Interface cannot be null or undefined.')
} else if (isEmpty(interfaceDefine)) {
throw new Error('Interface cannot be empty object.')
} else if (!Vue3Component.validateInterface(interfaceDefine)) {
throw new Error('The incoming parameter must be an interface.')
}
}

/**
* extend
* @static
* @method
* @param {VueComponent} componentDefine componentDefine to be inherited.
* @param {VueComponent} override override to be override.
* @description
*/
static extend (componentDefine, override) {
const vue3Component = isNotNil(componentDefine)
? Object.assign(new Vue3Component(), clone(componentDefine))
: new Vue3Component()

return isNotNil(override)
? Object.assign(vue3Component, clone(override))
: vue3Component
}

/**
* implement
* @static
* @method
* @public
* @param {VueComponent} interfaceDefine interfaceDefine to be inherited.
* @description Vue's interface can only define props、methods.
*/
static implement (interfaceDefine) {
Vue3Component.typeCheck(interfaceDefine)
return Vue3Component.extend(interfaceDefine)
}
static extend (extendOptions) {
const sub = new VueInheritance(extendOptions)
sub.extend(extendOptions.extends || {})
return sub

// eslint-disable-next-line no-useless-constructor
constructor (componentOptions) {
}

static implement = VueInheritance.extend

extend (extendOptions) {
const cloneExtendOptions = clone(extendOptions)
try {
this.props = mergeRight(this.props, cloneExtendOptions.props)
this.methods = mergeRight(this.methods, cloneExtendOptions.methods)
this.computed = mergeRight(this.computed, cloneExtendOptions.computed)
return this
} catch (e) {
throw new TypeError('extend error')
/**
* extends
* @type {VueComponent} extends
* @description property of Vue component option api.
*/
extends = null

/**
* implement
* @static
* @method
* @public
* @param {VueInterface} interface interface to be inherited.
* @description Vue's interface can only define props、methods.
*/
implement (interfaceDefine) {
Vue3Component.typeCheck(interfaceDefine)

if (isNil(this.extends) || isEmpty(this.extends)) {
this.extends = clone(interfaceDefine)
} else {
let deepestNode = this.extends
// find deepes node.
while (isNotNil(deepestNode.extends)) {
deepestNode = deepestNode.extends
}
deepestNode.extends = clone(interfaceDefine)
}
return this
}
}

/**
* VueInheritance
* @static
* @public
* @description
*/
const VueInheritance = Object.freeze({
extend: Vue3Component.extend,
implement: Vue3Component.implement
})

implement = this.extend
}
export default VueInheritance
18 changes: 18 additions & 0 deletions tests/controlGroup/IBorder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { IVisibility } from './IVisibility.js'

/**
* IBorder
* @author ocean.tsai
* @public
* @interface
* @description
*/
export const IBorder = {
extends: IVisibility,
props: {
borderSize: {
type: Number,
default: '1px'
}
}
}
25 changes: 25 additions & 0 deletions tests/controlGroup/IButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { IBorder } from './IBorder.js'

/**
* IButton
* @author ocean.tsai
* @public
* @interface
* @description
*/
export const IButton = {
extends: IBorder,

props: {
buttonSize: {
type: String,
default: 'sm'
}
},

methods: {
onClick () {
this.$emit('click', this.value)
}
}
}
15 changes: 15 additions & 0 deletions tests/controlGroup/IVisibility.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* IVisibility
* @author ocean.tsai
* @public
* @interface
* @description
*/
export const IVisibility = {
props: {
visible: {
type: Boolean,
default: true
}
}
}
15 changes: 15 additions & 0 deletions tests/experimentalGroup/IBorder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* IBorder
* @author ocean.tsai
* @public
* @interface
* @description
*/
export const IBorder = {
props: {
borderSize: {
type: Number,
default: '1px'
}
}
}
Loading