feat: update vue-adapter

This commit is contained in:
chaoling 2024-04-22 21:23:26 +08:00
parent b725b0d98c
commit 0f75e48f79
10 changed files with 408 additions and 46 deletions

View File

@ -1,32 +0,0 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
'use strict';
module.exports = {
printWidth: 120, // 一行120字符数如果超过会进行换行
tabWidth: 2, // tab等2个空格
useTabs: false, // 用空格缩进行
semi: true, // 行尾使用分号
singleQuote: true, // 字符串使用单引号
quoteProps: 'as-needed', // 仅在需要时在对象属性添加引号
jsxSingleQuote: false, // 在JSX中使用双引号
trailingComma: 'es5', // 使用尾逗号(对象、数组等)
bracketSpacing: true, // 对象的括号间增加空格
bracketSameLine: false, // 将多行JSX元素的>放在最后一行的末尾
arrowParens: 'avoid', // 在唯一的arrow函数参数周围省略括号
vueIndentScriptAndStyle: false, // 不缩进Vue文件中的<script>和<style>标记内的代码
endOfLine: 'lf', // 仅限换行(\n
};

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
module.exports = {
presets: ['@babel/preset-typescript', ['@babel/preset-env', { targets: { node: 'current' } }]],
plugins: [
'@babel/plugin-syntax-jsx',
[
'@babel/plugin-transform-react-jsx',
{
runtime: 'automatic',
importSource: 'openinula',
},
],
['@babel/plugin-proposal-class-properties', { loose: true }],
['@babel/plugin-proposal-private-methods', { loose: true }],
['@babel/plugin-proposal-private-property-in-object', { loose: true }],
'@babel/plugin-transform-object-assign',
'@babel/plugin-transform-object-super',
['@babel/plugin-proposal-object-rest-spread', { loose: true, useBuiltIns: true }],
['@babel/plugin-transform-template-literals', { loose: true }],
'@babel/plugin-transform-arrow-functions',
'@babel/plugin-transform-literals',
'@babel/plugin-transform-for-of',
'@babel/plugin-transform-block-scoped-functions',
'@babel/plugin-transform-classes',
'@babel/plugin-transform-shorthand-properties',
'@babel/plugin-transform-computed-properties',
'@babel/plugin-transform-parameters',
['@babel/plugin-transform-spread', { loose: true, useBuiltIns: true }],
['@babel/plugin-transform-block-scoping', { throwIfClosureRequired: false }],
['@babel/plugin-transform-destructuring', { loose: true, useBuiltIns: true }],
'@babel/plugin-transform-runtime',
'@babel/plugin-proposal-nullish-coalescing-operator',
'@babel/plugin-proposal-optional-chaining',
],
};

View File

@ -2,18 +2,60 @@
"name": "@inula/vue-adapter",
"version": "0.0.1",
"description": "vue adapter",
"main": "./src/index.ts",
"main": "./build/cjs/vue-adapter.js",
"module": "./build/esm/vue-adapter.js",
"types": "build/@types/index.d.ts",
"files": [
"/build",
"README.md"
],
"scripts": {
"test": "vitest --ui"
"test": "vitest --ui",
"build": "rollup -c ./scripts/rollup.config.js && npm run build-types",
"build-types": "tsc -p tsconfig.build.json && rollup -c ./scripts/build-types.js"
},
"dependencies": {
"openinula": "workspace:*"
},
"devDependencies": {
"@testing-library/user-event": "^12.1.10",
"@babel/core": "7.21.3",
"@babel/plugin-proposal-class-properties": "7.16.7",
"@babel/plugin-proposal-nullish-coalescing-operator": "7.16.7",
"@babel/plugin-proposal-object-rest-spread": "7.16.7",
"@babel/plugin-proposal-optional-chaining": "7.16.7",
"@babel/plugin-syntax-jsx": "7.16.7",
"@babel/plugin-transform-arrow-functions": "7.16.7",
"@babel/plugin-transform-block-scoped-functions": "7.16.7",
"@babel/plugin-transform-block-scoping": "7.16.7",
"@babel/plugin-transform-classes": "7.16.7",
"@babel/plugin-transform-computed-properties": "7.16.7",
"@babel/plugin-transform-destructuring": "7.16.7",
"@babel/plugin-transform-for-of": "7.16.7",
"@babel/plugin-transform-literals": "7.16.7",
"@babel/plugin-transform-object-assign": "7.16.7",
"@babel/plugin-transform-object-super": "7.16.7",
"@babel/plugin-transform-parameters": "7.16.7",
"@babel/plugin-transform-react-jsx": "7.16.7",
"@babel/plugin-transform-react-jsx-source": "^7.16.7",
"@babel/plugin-transform-runtime": "7.16.7",
"@babel/plugin-transform-shorthand-properties": "7.16.7",
"@babel/plugin-transform-spread": "7.16.7",
"@babel/plugin-transform-template-literals": "7.16.7",
"@babel/preset-env": "7.16.7",
"@babel/preset-typescript": "^7.16.7",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-node-resolve": "^15.1.0",
"prettier": "2.8.8",
"rollup": "2.79.1",
"rollup-plugin-dts": "^6.0.1",
"rollup-plugin-terser": "^5.1.3",
"typescript": "4.9.3",
"@vitest/ui": "^0.34.5",
"jsdom": "^24.0.0",
"vitest": "^0.34.5",
"@vitejs/plugin-react": "^4.2.1"
},
"peerDependencies": {
"openinula": ">=0.1.1"
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
import fs from 'fs';
import path from 'path';
import dts from 'rollup-plugin-dts';
function deleteFolder(filePath) {
if (fs.existsSync(filePath)) {
if (fs.lstatSync(filePath).isDirectory()) {
const files = fs.readdirSync(filePath);
files.forEach(file => {
const nextFilePath = path.join(filePath, file);
const states = fs.lstatSync(nextFilePath);
if (states.isDirectory()) {
deleteFolder(nextFilePath);
} else {
fs.unlinkSync(nextFilePath);
}
});
fs.rmdirSync(filePath);
} else if (fs.lstatSync(filePath).isFile()) {
fs.unlinkSync(filePath);
}
}
}
/**
* 删除非空文件夹
* @param folders {string[]}
* @returns {{buildEnd(): void, name: string}}
*/
export function cleanUp(folders) {
return {
name: 'clean-up',
buildEnd() {
folders.forEach(f => deleteFolder(f));
},
};
}
function buildTypeConfig() {
return {
input: ['./build/@types/index.d.ts'],
output: {
file: './build/@types/index.d.ts',
},
plugins: [dts(), cleanUp(['./build/@types/'])],
};
}
export default [buildTypeConfig()];

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
import babel from '@rollup/plugin-babel';
import nodeResolve from '@rollup/plugin-node-resolve';
import { terser } from 'rollup-plugin-terser';
const rootDir = path.join(__dirname, '..');
const outDir = path.join(rootDir, 'build');
const extensions = ['.js', '.ts', '.tsx'];
if (!fs.existsSync(outDir)) {
fs.mkdirSync(outDir, { recursive: true });
}
const getConfig = mode => {
const prod = mode.startsWith('prod');
const outputList = [
{
file: path.join(outDir, `cjs/vue-adapter.${prod ? 'min.' : ''}js`),
sourcemap: 'true',
format: 'cjs',
},
{
file: path.join(outDir, `umd/vue-adapter.${prod ? 'min.' : ''}js`),
name: 'VueAdapter',
sourcemap: 'true',
format: 'umd',
},
];
if (!prod) {
outputList.push({
file: path.join(outDir, 'esm/vue-adapter.js'),
sourcemap: 'true',
format: 'esm',
});
}
return {
input: path.join(rootDir, '/src/index.ts'),
output: outputList,
plugins: [
nodeResolve({
extensions,
modulesOnly: true,
}),
babel({
exclude: 'node_modules/**',
configFile: path.join(rootDir, '/babel.config.js'),
babelHelpers: 'runtime',
extensions,
}),
prod && terser(),
],
};
};
export default [getConfig('dev'), getConfig('prod')];

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
*
* openInula is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.

View File

@ -23,7 +23,7 @@ const useIsMounted = () => {
isMounted.current = false;
};
}, []);
return isMounted;
return isMounted.current;
};
export const onBeforeMount = (fn: () => void) => {

View File

@ -17,9 +17,113 @@
import { describe, it, vi, expect } from 'vitest';
import { render, act, useState } from 'openinula';
import { onBeforeUnmount } from '../src';
import { onBeforeUnmount, onUnmounted, onMounted, onBeforeMount, onUpdated } from '../src';
describe('lifecycle', () => {
it('should call the onBeforeMount', () => {
const fn = vi.fn(() => {
expect(document.querySelector('span')).toBeNull();
});
const Comp = () => {
const [toggle, setToggle] = useState(true);
return (
<>
{toggle ? <Child /> : null}
<button onClick={() => setToggle(false)}>Unmount</button>
</>
);
};
const Child = () => {
onBeforeMount(fn);
return <span />;
};
const container = document.createElement('div');
document.body.appendChild(container);
render(<Comp />, container);
expect(document.querySelector('span')).not.toBeNull();
act(() => {
container.querySelector('button').dispatchEvent(new MouseEvent('click', { bubbles: true }));
});
expect(fn).toHaveBeenCalledTimes(1);
});
it('should call the onMounted', () => {
const fn = vi.fn(() => {
// 断言在组件卸载之后,子组件不存在于 DOM 中
expect(document.querySelector('span')).not.toBeNull();
});
const Comp = () => {
const [toggle, setToggle] = useState(true);
return (
<>
{toggle ? <Child /> : null}
<button onClick={() => setToggle(false)}>Unmount</button>
</>
);
};
const Child = () => {
onMounted(fn);
return <span />;
};
const container = document.createElement('div');
document.body.appendChild(container);
render(<Comp />, container);
expect(document.querySelector('span')).not.toBeNull();
act(() => {
container.querySelector('button').dispatchEvent(new MouseEvent('click', { bubbles: true }));
});
expect(fn).toHaveBeenCalledTimes(1);
});
it('should call the onUnmounted after the component unmounts', () => {
const fn = vi.fn(() => {
// 断言在组件卸载之后,子组件不存在于 DOM 中
expect(document.querySelector('span')).not.toBeNull();
});
const Comp = () => {
const [toggle, setToggle] = useState(true);
return (
<>
{toggle ? <Child /> : null}
<button onClick={() => setToggle(false)}>Unmount</button>
</>
);
};
const Child = () => {
onUnmounted(fn);
return <span />;
};
const container = document.createElement('div');
document.body.appendChild(container);
render(<Comp />, container);
expect(document.querySelector('span')).not.toBeNull();
act(() => {
container.querySelector('button').dispatchEvent(new MouseEvent('click', { bubbles: true }));
});
expect(fn).toHaveBeenCalledTimes(1);
});
it('should call the onBeforeUnmount before the component unmounts', () => {
const fn = vi.fn(() => {
// 断言在组件卸载之前,子组件仍然存在于 DOM 中
@ -56,4 +160,34 @@ describe('lifecycle', () => {
expect(document.querySelector('span')).toBeNull();
});
it('should call the onUpdated/onBeforeUpdated', () => {
const fn = vi.fn(() => {
expect(document.querySelector('span').outerHTML).toBe('<span>0</span>');
});
const Comp = () => {
const [toggle, setToggle] = useState(true);
onUpdated(fn);
return (
<>
<span>{toggle ? 1 : 0}</span>
<button onClick={() => setToggle(false)}>Unmount</button>
</>
);
};
const container = document.createElement('div');
document.body.appendChild(container);
render(<Comp />, container);
expect(fn).toHaveBeenCalledTimes(0);
expect(document.querySelector('span').outerHTML).toBe('<span>1</span>');
container.querySelector('button').dispatchEvent(new MouseEvent('click', { bubbles: true }));
expect(fn).toHaveBeenCalledTimes(1);
});
});

View File

@ -0,0 +1,10 @@
{
"files": [
"src/index.ts"
],
"extends": "./tsconfig.json",
"compilerOptions": {
"emitDeclarationOnly": true,
"declarationDir": "./build/@types"
},
}

View File

@ -1,13 +1,35 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"lib": ["ESNext", "DOM"],
"moduleResolution": "Node",
"strict": true,
"outDir": "./build",
"incremental": false,
"sourceMap": true,
"allowJs": true, // allowJs=true => tsc compile js as module, no type check
"checkJs": false, // Disable ts error checking in js
"strict": true, // js-ts mixed setting
"noImplicitReturns": true,
"noUnusedLocals": false, // jsts.
"noUnusedParameters": false,
"noImplicitAny": false,
"noImplicitThis": true,
"module": "CommonJS",
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"target": "es5",
"jsx": "preserve",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"allowUnreachableCode": true,
"alwaysStrict": true,
"esModuleInterop": true,
"declaration": true,
"experimentalDecorators": true,
"downlevelIteration": true,
"types": ["jest"], // 使@types/node
"lib": ["dom", "esnext", "ES2015", "ES2016", "ES2017", "ES2018", "ES2019", "ES2020"],
"baseUrl": ".",
"rootDir": "./src",
"strictNullChecks": true
},
"ts-node": {
"esm": true
}
"exclude": ["node_modules", "**/*.spec.ts"]
}