Feat/plugin rax compat (#262)

* feat: support inlineStyle option for plugin rax compat

* fix: update pnpm lock yaml

* refactor: rax-compat create-element and plugin rule

* chore: remove some lint problem

* chore: update pnpm lock

* chore: update pnpm

* feat: add warn for inline style
This commit is contained in:
ZeroLing 2022-06-20 15:28:01 +08:00 committed by GitHub
parent e42a9ba67a
commit 59aa271de1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 18379 additions and 1472 deletions

View File

@ -1,15 +1,22 @@
# @ice/plugin-rax-compat
ice plugin for migrate `rax-app` project to `ice`.
ICE plugin for migrating `rax-app` project into `ICE`.
## Usage
add plugin in `ice.config.ts`:
```js
import raxCompat from '@ice/plugin-rax-compat';
import compatRax from '@ice/plugin-rax-compat';
export default {
plugins: [raxCompat()],
}
export default defineConfig({
plugins: [compatRax(options)],
});
```
## Options
- inlineStyle:
- Enable stylesheet loader to import css file.
- default to `false`

View File

@ -17,10 +17,13 @@
"!esm/**/*.map"
],
"dependencies": {
"rax-compat": "^0.1.0"
"consola": "^2.15.3",
"rax-compat": "^0.1.0",
"stylesheet-loader": "^0.9.1"
},
"devDependencies": {
"@ice/types": "^1.0.0"
"@ice/types": "^1.0.0",
"@types/webpack": "^5.28.0"
},
"repository": {
"type": "http",

View File

@ -1,11 +1,11 @@
import { createRequire } from 'module';
import type { Plugin } from '@ice/types';
import type { RuleSetRule } from 'webpack';
import consola from 'consola';
const require = createRequire(import.meta.url);
const plugin: Plugin = ({ onGetConfig }) => {
onGetConfig((config) => {
Object.assign(config.alias, {
const alias = {
// Add rax compat packages.
rax: require.resolve('rax-compat'),
'rax-children': require.resolve('rax-compat/children'),
@ -16,11 +16,56 @@ const plugin: Plugin = ({ onGetConfig }) => {
'rax-find-dom-node': require.resolve('rax-compat/find-dom-node'),
'rax-is-valid-element': require.resolve('rax-compat/is-valid-element'),
'rax-unmount-component-at-node': require.resolve('rax-compat/unmount-component-at-node'),
});
});
};
export default () => ({
const ruleSetStylesheet = {
test: /\.css$/i,
use: [
{
loader: require.resolve('stylesheet-loader'),
options: {},
},
],
};
function getPlugin(options: CompatRaxOptions): Plugin {
return ({ onGetConfig }) => {
onGetConfig((config) => {
Object.assign(config.alias, alias);
if (options.inlineStyle) {
consola.warn('[WARN] Enabling inline style is not recommended.');
consola.warn(' It is recommended to use CSS modules (as default). Only allow old projects to migrate and use.');
config.configureWebpack ??= [];
config.configureWebpack.unshift((config) => {
const { rules } = config.module || {};
if (Array.isArray(rules)) {
for (let i = 0, l = rules.length; i < l; i++) {
const rule: RuleSetRule | any = rules[i];
// Find the css rule, that default to CSS Modules.
if (rule.test && rule.test.source.indexOf('.css') > -1) {
rule.test = /\.module\.css$/i;
rules[i] = {
test: /\.css$/i,
oneOf: [
rule,
ruleSetStylesheet,
],
};
}
}
}
return config;
});
}
});
};
}
export interface CompatRaxOptions {
inlineStyle?: boolean;
}
export default (options: CompatRaxOptions | void) => ({
name: '@ice/plugin-rax-compat',
plugin,
plugin: getPlugin(options || {}),
});

View File

@ -5,9 +5,9 @@ import type {
ReactNode,
RefObject,
} from 'react';
import { createElement as _createElement, useEffect, useRef, forwardRef } from 'react';
import { createElement as _createElement, useEffect, forwardRef } from 'react';
import { setupAppear } from 'appear-polyfill';
import { cached } from 'style-unit';
import { cached, convertUnit } from 'style-unit';
import { isFunction, isObject, isNumber } from './type';
let appearSetup = false;
@ -18,8 +18,6 @@ function setupAppearOnce() {
}
}
const hasOwn = {}.hasOwnProperty;
// https://github.com/alibaba/rax/blob/master/packages/driver-dom/src/index.js
// opacity -> opa
// fontWeight -> ntw
@ -55,19 +53,23 @@ export function createElement<P extends {
type: FunctionComponent<P>,
props?: Attributes & P | null,
...children: ReactNode[]): ReactElement {
const { children: propsChildren, onAppear, onDisappear } = props || {};
const rest = Object.assign({}, props);
const { children: propsChildren, onAppear, onDisappear } = rest;
delete rest.children;
delete rest.onAppear;
delete rest.onDisappear;
// Compat for style unit.
rest.style = compatStyle(rest.style);
const compatStyleProps = compatStyle(rest.style);
if (compatStyleProps) {
rest.style = compatStyleProps;
}
rest.ref = props.ref || useRef(null);
const args = [type, rest as Attributes & P | null, propsChildren];
// Create backend element.
const args = [type, rest, propsChildren];
let element: any = _createElement.apply(null, args.concat(children));
// Polyfill onAppear and onDisappear.
// Polyfill for appear and disappear event.
if (isFunction(onAppear) || isFunction(onDisappear)) {
setupAppearOnce();
element = _createElement(forwardRef(AppearOrDisappear), {
@ -82,19 +84,24 @@ export function createElement<P extends {
const isDimensionalProp = cached((prop: string) => !NON_DIMENSIONAL_REG.test(prop));
// Convert numeric value into rpx.
// eg. width: 2px ->
function compatStyle(style?: object): any {
// Convert unit as driver-dom does.
// https://github.com/alibaba/rax/blob/master/packages/driver-dom/src/index.js#L346
function compatStyle<S = object>(style?: S): S | void {
if (isObject(style)) {
const result = {};
// Do not modify the original style object, copy results to another plain object.
const result = Object.create(null);
for (let key in style) {
if (hasOwn.call(style, key)) {
// @ts-ignore
if (isNumber(style[key]) && isDimensionalProp(key)) result[key] = `${style[key]}rpx`;
const value = style[key];
if (isNumber(value) && isDimensionalProp(key)) {
// Transform rpx to vw.
result[key] = convertUnit(`${value}rpx`);
} else {
result[key] = convertUnit(value);
}
}
return result;
}
return style;
}
// Appear HOC Component.
@ -106,6 +113,7 @@ function AppearOrDisappear(props: any, ref: RefObject<EventTarget>) {
function listen(eventName: string, handler: EventListenerOrEventListenerObject) {
if (isFunction(handler) && ref != null) {
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
const { current } = ref;
if (current != null) {

File diff suppressed because it is too large Load Diff

15828
pnpm-lock.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -38,11 +38,12 @@ async function checkVersionExists(pkg: string, version: string, distTag: string)
}
export function getVersionPrefix(version): string {
return isNaN(version[0]) ? version[0] : '';
return Number.isNaN(version[0]) ? version[0] : '';
}
export async function getPackageInfos(distTag = ''): Promise<IPackageInfo[]> {
const packageInfos: IPackageInfo[] = [];
// eslint-disable-next-line no-negated-condition
if (!existsSync(TARGET_DIRECTORY)) {
console.log(`[ERROR] Directory ${TARGET_DIRECTORY} not exist!`);
} else {