parent
83c80341dc
commit
d599b36eaa
|
@ -0,0 +1,92 @@
|
|||
# delight-transformer
|
||||
|
||||
This is a experimental package to implement [API2.0](https://gitee.com/openInula/rfcs/blob/master/src/002-zouyu-API2.0.md) to [dlight](https://github.com/dlight-js/dlight) class.
|
||||
|
||||
## Todo-list
|
||||
|
||||
- [ ] function 2 class.
|
||||
- [x] assignment 2 property
|
||||
- [x] statement 2 watch func
|
||||
- [ ] handle `props` @HQ
|
||||
- [x] object destructuring
|
||||
- [x] default value
|
||||
- [ ] partial object destructuring
|
||||
- [ ] nested object destructuring
|
||||
- [ ] nested array destructuring
|
||||
- [ ] alias
|
||||
- [x] add `this` @HQ
|
||||
- [ ] for (jsx-parser) -> playground + benchmark @YH
|
||||
- [ ] lifecycle @HQ
|
||||
- [ ] ref @HQ (to validate)
|
||||
- [ ] env @HQ (to validate)
|
||||
- [ ] Sub component
|
||||
- [ ] Early Return
|
||||
- [ ] custom hook -> Model @YH
|
||||
- [ ] JSX
|
||||
- [x] style
|
||||
- [x] fragment
|
||||
- [ ] ref (to validate)
|
||||
- [ ] snippet
|
||||
- [ ] for
|
||||
|
||||
# function component syntax
|
||||
|
||||
- [ ] props (destructuring | partial destructuring | default value | alias)
|
||||
- [ ] variable declaration -> class component property
|
||||
- [ ] function declaration ( arrow function | async function )-> class method
|
||||
- [ ] Statement -> watch function
|
||||
- [ ] assignment
|
||||
- [ ] function call
|
||||
- [ ] class method call
|
||||
- [ ] for loop
|
||||
- [ ] while loop (do while, while, for, for in, for of)
|
||||
- [ ] if statement
|
||||
- [ ] switch statement
|
||||
- [ ] try catch statement
|
||||
- [ ] throw statement ? not support
|
||||
- [ ] delete expression
|
||||
- [ ] lifecycle -> LabeledStatement
|
||||
- [ ] return statement -> render method(Body)
|
||||
- [ ] iife
|
||||
- [ ] early return
|
||||
|
||||
|
||||
# custom hook syntax
|
||||
TODO
|
||||
|
||||
# issues
|
||||
- [ ] partial props destructuring -> support this.$props @YH
|
||||
```jsx
|
||||
function Input({onClick, xxx, ...props}) {
|
||||
function handleClick() {
|
||||
onClick()
|
||||
}
|
||||
return <input onClick={handleClick} {...props} />
|
||||
}
|
||||
```
|
||||
- [ ] model class declaration should before class component declaration -> use Class polyfill
|
||||
```jsx
|
||||
// Code like this will cause error: `FetchModel` is not defined
|
||||
@Main
|
||||
@View
|
||||
class MyComp {
|
||||
fetchModel = use(FetchModel, { url: "https://api.example.com/data" })
|
||||
|
||||
Body() {}
|
||||
}
|
||||
|
||||
@Model
|
||||
class FetchModel {}
|
||||
```
|
||||
- [ ] custom hook early return @YH
|
||||
- [ ] snippet
|
||||
```jsx
|
||||
const H1 = <h1></h1>;
|
||||
// {H1}
|
||||
const H1 = (name) => <h1 className={name}></h1>;
|
||||
// {H1()} <H1/>
|
||||
function H1() {
|
||||
return <h1></h1>;
|
||||
}
|
||||
// <H1/>
|
||||
```
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"name": "babel-preset-inula-next",
|
||||
"version": "0.0.1",
|
||||
"author": {
|
||||
"name": "IanDx",
|
||||
"email": "iandxssxx@gmail.com"
|
||||
},
|
||||
"keywords": [
|
||||
"dlight.js",
|
||||
"babel-preset"
|
||||
],
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"type": "module",
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsup --sourcemap",
|
||||
"test": "vitest --ui"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/babel__core": "^7.20.5",
|
||||
"@types/node": "^20.10.5",
|
||||
"tsup": "^6.7.0",
|
||||
"typescript": "^5.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-syntax-decorators": "^7.23.3",
|
||||
"@babel/core": "^7.23.3",
|
||||
"@babel/plugin-syntax-jsx": "7.16.7",
|
||||
"@babel/plugin-syntax-typescript": "^7.23.3",
|
||||
"@inula/reactivity-parser": "workspace:*",
|
||||
"@inula/view-generator": "workspace:*",
|
||||
"@inula/view-parser": "workspace:*",
|
||||
"@inula/class-transformer": "workspace:*",
|
||||
"jsx-view-parser": "workspace:*",
|
||||
"minimatch": "^9.0.3",
|
||||
"vitest": "^1.4.0"
|
||||
},
|
||||
"tsup": {
|
||||
"entry": [
|
||||
"src/index.ts"
|
||||
],
|
||||
"format": [
|
||||
"cjs",
|
||||
"esm"
|
||||
],
|
||||
"clean": true,
|
||||
"dts": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,490 @@
|
|||
export const devMode = process.env.NODE_ENV === 'development';
|
||||
|
||||
export const alterAttributeMap = {
|
||||
class: 'className',
|
||||
for: 'htmlFor',
|
||||
};
|
||||
|
||||
export const reactivityFuncNames = [
|
||||
// ---- Array
|
||||
'push',
|
||||
'pop',
|
||||
'shift',
|
||||
'unshift',
|
||||
'splice',
|
||||
'sort',
|
||||
'reverse',
|
||||
// ---- Set
|
||||
'add',
|
||||
'delete',
|
||||
'clear',
|
||||
// ---- Map
|
||||
'set',
|
||||
'delete',
|
||||
'clear',
|
||||
];
|
||||
|
||||
export const defaultHTMLTags = [
|
||||
'a',
|
||||
'abbr',
|
||||
'address',
|
||||
'area',
|
||||
'article',
|
||||
'aside',
|
||||
'audio',
|
||||
'b',
|
||||
'base',
|
||||
'bdi',
|
||||
'bdo',
|
||||
'blockquote',
|
||||
'body',
|
||||
'br',
|
||||
'button',
|
||||
'canvas',
|
||||
'caption',
|
||||
'cite',
|
||||
'code',
|
||||
'col',
|
||||
'colgroup',
|
||||
'data',
|
||||
'datalist',
|
||||
'dd',
|
||||
'del',
|
||||
'details',
|
||||
'dfn',
|
||||
'dialog',
|
||||
'div',
|
||||
'dl',
|
||||
'dt',
|
||||
'em',
|
||||
'embed',
|
||||
'fieldset',
|
||||
'figcaption',
|
||||
'figure',
|
||||
'footer',
|
||||
'form',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'head',
|
||||
'header',
|
||||
'hgroup',
|
||||
'hr',
|
||||
'html',
|
||||
'i',
|
||||
'iframe',
|
||||
'img',
|
||||
'input',
|
||||
'ins',
|
||||
'kbd',
|
||||
'label',
|
||||
'legend',
|
||||
'li',
|
||||
'link',
|
||||
'main',
|
||||
'map',
|
||||
'mark',
|
||||
'menu',
|
||||
'meta',
|
||||
'meter',
|
||||
'nav',
|
||||
'noscript',
|
||||
'object',
|
||||
'ol',
|
||||
'optgroup',
|
||||
'option',
|
||||
'output',
|
||||
'p',
|
||||
'picture',
|
||||
'pre',
|
||||
'progress',
|
||||
'q',
|
||||
'rp',
|
||||
'rt',
|
||||
'ruby',
|
||||
's',
|
||||
'samp',
|
||||
'script',
|
||||
'section',
|
||||
'select',
|
||||
'slot',
|
||||
'small',
|
||||
'source',
|
||||
'span',
|
||||
'strong',
|
||||
'style',
|
||||
'sub',
|
||||
'summary',
|
||||
'sup',
|
||||
'table',
|
||||
'tbody',
|
||||
'td',
|
||||
'template',
|
||||
'textarea',
|
||||
'tfoot',
|
||||
'th',
|
||||
'thead',
|
||||
'time',
|
||||
'title',
|
||||
'tr',
|
||||
'track',
|
||||
'u',
|
||||
'ul',
|
||||
'var',
|
||||
'video',
|
||||
'wbr',
|
||||
'acronym',
|
||||
'applet',
|
||||
'basefont',
|
||||
'bgsound',
|
||||
'big',
|
||||
'blink',
|
||||
'center',
|
||||
'dir',
|
||||
'font',
|
||||
'frame',
|
||||
'frameset',
|
||||
'isindex',
|
||||
'keygen',
|
||||
'listing',
|
||||
'marquee',
|
||||
'menuitem',
|
||||
'multicol',
|
||||
'nextid',
|
||||
'nobr',
|
||||
'noembed',
|
||||
'noframes',
|
||||
'param',
|
||||
'plaintext',
|
||||
'rb',
|
||||
'rtc',
|
||||
'spacer',
|
||||
'strike',
|
||||
'tt',
|
||||
'xmp',
|
||||
'animate',
|
||||
'animateMotion',
|
||||
'animateTransform',
|
||||
'circle',
|
||||
'clipPath',
|
||||
'defs',
|
||||
'desc',
|
||||
'ellipse',
|
||||
'feBlend',
|
||||
'feColorMatrix',
|
||||
'feComponentTransfer',
|
||||
'feComposite',
|
||||
'feConvolveMatrix',
|
||||
'feDiffuseLighting',
|
||||
'feDisplacementMap',
|
||||
'feDistantLight',
|
||||
'feDropShadow',
|
||||
'feFlood',
|
||||
'feFuncA',
|
||||
'feFuncB',
|
||||
'feFuncG',
|
||||
'feFuncR',
|
||||
'feGaussianBlur',
|
||||
'feImage',
|
||||
'feMerge',
|
||||
'feMergeNode',
|
||||
'feMorphology',
|
||||
'feOffset',
|
||||
'fePointLight',
|
||||
'feSpecularLighting',
|
||||
'feSpotLight',
|
||||
'feTile',
|
||||
'feTurbulence',
|
||||
'filter',
|
||||
'foreignObject',
|
||||
'g',
|
||||
'image',
|
||||
'line',
|
||||
'linearGradient',
|
||||
'marker',
|
||||
'mask',
|
||||
'metadata',
|
||||
'mpath',
|
||||
'path',
|
||||
'pattern',
|
||||
'polygon',
|
||||
'polyline',
|
||||
'radialGradient',
|
||||
'rect',
|
||||
'set',
|
||||
'stop',
|
||||
'svg',
|
||||
'switch',
|
||||
'symbol',
|
||||
'text',
|
||||
'textPath',
|
||||
'tspan',
|
||||
'use',
|
||||
'view',
|
||||
];
|
||||
|
||||
export const availableDecoNames = ['Static', 'Prop', 'Env', 'Content', 'Children'];
|
||||
export const dlightDefaultPackageName = '@inula/next';
|
||||
|
||||
export const importMap = Object.fromEntries(
|
||||
[
|
||||
'createElement',
|
||||
'setStyle',
|
||||
'setDataset',
|
||||
'setEvent',
|
||||
'delegateEvent',
|
||||
'setHTMLProp',
|
||||
'setHTMLAttr',
|
||||
'setHTMLProps',
|
||||
'setHTMLAttrs',
|
||||
'createTextNode',
|
||||
'updateText',
|
||||
'insertNode',
|
||||
'ForNode',
|
||||
'CondNode',
|
||||
'ExpNode',
|
||||
'EnvNode',
|
||||
'TryNode',
|
||||
'SnippetNode',
|
||||
'PropView',
|
||||
'render',
|
||||
].map(name => [name, `$$${name}`])
|
||||
);
|
||||
|
||||
export const importsToDelete = [
|
||||
'Static',
|
||||
'Children',
|
||||
'Content',
|
||||
'Prop',
|
||||
'Env',
|
||||
'Watch',
|
||||
'ForwardProps',
|
||||
'Main',
|
||||
'App',
|
||||
'Mount',
|
||||
'_',
|
||||
'env',
|
||||
'Snippet',
|
||||
...defaultHTMLTags.filter(tag => tag !== 'use'),
|
||||
];
|
||||
|
||||
/**
|
||||
* @brief HTML internal attribute map, can be accessed as js property
|
||||
*/
|
||||
export const defaultAttributeMap = {
|
||||
// ---- Other property as attribute
|
||||
textContent: ['*'],
|
||||
innerHTML: ['*'],
|
||||
// ---- Source: https://developer.mozilla.org/zh-CN/docs/Web/HTML/Attributes
|
||||
accept: ['form', 'input'],
|
||||
// ---- Original: accept-charset
|
||||
acceptCharset: ['form'],
|
||||
accesskey: ['*'],
|
||||
action: ['form'],
|
||||
align: ['caption', 'col', 'colgroup', 'hr', 'iframe', 'img', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr'],
|
||||
allow: ['iframe'],
|
||||
alt: ['area', 'img', 'input'],
|
||||
async: ['script'],
|
||||
autocapitalize: ['*'],
|
||||
autocomplete: ['form', 'input', 'select', 'textarea'],
|
||||
autofocus: ['button', 'input', 'select', 'textarea'],
|
||||
autoplay: ['audio', 'video'],
|
||||
background: ['body', 'table', 'td', 'th'],
|
||||
// ---- Original: base
|
||||
bgColor: ['body', 'col', 'colgroup', 'marquee', 'table', 'tbody', 'tfoot', 'td', 'th', 'tr'],
|
||||
border: ['img', 'object', 'table'],
|
||||
buffered: ['audio', 'video'],
|
||||
capture: ['input'],
|
||||
charset: ['meta'],
|
||||
checked: ['input'],
|
||||
cite: ['blockquote', 'del', 'ins', 'q'],
|
||||
className: ['*'],
|
||||
color: ['font', 'hr'],
|
||||
cols: ['textarea'],
|
||||
// ---- Original: colspan
|
||||
colSpan: ['td', 'th'],
|
||||
content: ['meta'],
|
||||
// ---- Original: contenteditable
|
||||
contentEditable: ['*'],
|
||||
contextmenu: ['*'],
|
||||
controls: ['audio', 'video'],
|
||||
coords: ['area'],
|
||||
crossOrigin: ['audio', 'img', 'link', 'script', 'video'],
|
||||
csp: ['iframe'],
|
||||
data: ['object'],
|
||||
// ---- Original: datetime
|
||||
dateTime: ['del', 'ins', 'time'],
|
||||
decoding: ['img'],
|
||||
default: ['track'],
|
||||
defer: ['script'],
|
||||
dir: ['*'],
|
||||
dirname: ['input', 'textarea'],
|
||||
disabled: ['button', 'fieldset', 'input', 'optgroup', 'option', 'select', 'textarea'],
|
||||
download: ['a', 'area'],
|
||||
draggable: ['*'],
|
||||
enctype: ['form'],
|
||||
// ---- Original: enterkeyhint
|
||||
enterKeyHint: ['textarea', 'contenteditable'],
|
||||
htmlFor: ['label', 'output'],
|
||||
form: ['button', 'fieldset', 'input', 'label', 'meter', 'object', 'output', 'progress', 'select', 'textarea'],
|
||||
// ---- Original: formaction
|
||||
formAction: ['input', 'button'],
|
||||
// ---- Original: formenctype
|
||||
formEnctype: ['button', 'input'],
|
||||
// ---- Original: formmethod
|
||||
formMethod: ['button', 'input'],
|
||||
// ---- Original: formnovalidate
|
||||
formNoValidate: ['button', 'input'],
|
||||
// ---- Original: formtarget
|
||||
formTarget: ['button', 'input'],
|
||||
headers: ['td', 'th'],
|
||||
height: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'],
|
||||
hidden: ['*'],
|
||||
high: ['meter'],
|
||||
href: ['a', 'area', 'base', 'link'],
|
||||
hreflang: ['a', 'link'],
|
||||
// ---- Original: http-equiv
|
||||
httpEquiv: ['meta'],
|
||||
id: ['*'],
|
||||
integrity: ['link', 'script'],
|
||||
// ---- Original: intrinsicsize
|
||||
intrinsicSize: ['img'],
|
||||
// ---- Original: inputmode
|
||||
inputMode: ['textarea', 'contenteditable'],
|
||||
ismap: ['img'],
|
||||
// ---- Original: itemprop
|
||||
itemProp: ['*'],
|
||||
kind: ['track'],
|
||||
label: ['optgroup', 'option', 'track'],
|
||||
lang: ['*'],
|
||||
language: ['script'],
|
||||
loading: ['img', 'iframe'],
|
||||
list: ['input'],
|
||||
loop: ['audio', 'marquee', 'video'],
|
||||
low: ['meter'],
|
||||
manifest: ['html'],
|
||||
max: ['input', 'meter', 'progress'],
|
||||
// ---- Original: maxlength
|
||||
maxLength: ['input', 'textarea'],
|
||||
// ---- Original: minlength
|
||||
minLength: ['input', 'textarea'],
|
||||
media: ['a', 'area', 'link', 'source', 'style'],
|
||||
method: ['form'],
|
||||
min: ['input', 'meter'],
|
||||
multiple: ['input', 'select'],
|
||||
muted: ['audio', 'video'],
|
||||
name: [
|
||||
'button',
|
||||
'form',
|
||||
'fieldset',
|
||||
'iframe',
|
||||
'input',
|
||||
'object',
|
||||
'output',
|
||||
'select',
|
||||
'textarea',
|
||||
'map',
|
||||
'meta',
|
||||
'param',
|
||||
],
|
||||
// ---- Original: novalidate
|
||||
noValidate: ['form'],
|
||||
open: ['details', 'dialog'],
|
||||
optimum: ['meter'],
|
||||
pattern: ['input'],
|
||||
ping: ['a', 'area'],
|
||||
placeholder: ['input', 'textarea'],
|
||||
// ---- Original: playsinline
|
||||
playsInline: ['video'],
|
||||
poster: ['video'],
|
||||
preload: ['audio', 'video'],
|
||||
readonly: ['input', 'textarea'],
|
||||
// ---- Original: referrerpolicy
|
||||
referrerPolicy: ['a', 'area', 'iframe', 'img', 'link', 'script'],
|
||||
rel: ['a', 'area', 'link'],
|
||||
required: ['input', 'select', 'textarea'],
|
||||
reversed: ['ol'],
|
||||
role: ['*'],
|
||||
rows: ['textarea'],
|
||||
// ---- Original: rowspan
|
||||
rowSpan: ['td', 'th'],
|
||||
sandbox: ['iframe'],
|
||||
scope: ['th'],
|
||||
scoped: ['style'],
|
||||
selected: ['option'],
|
||||
shape: ['a', 'area'],
|
||||
size: ['input', 'select'],
|
||||
sizes: ['link', 'img', 'source'],
|
||||
slot: ['*'],
|
||||
span: ['col', 'colgroup'],
|
||||
spellcheck: ['*'],
|
||||
src: ['audio', 'embed', 'iframe', 'img', 'input', 'script', 'source', 'track', 'video'],
|
||||
srcdoc: ['iframe'],
|
||||
srclang: ['track'],
|
||||
srcset: ['img', 'source'],
|
||||
start: ['ol'],
|
||||
step: ['input'],
|
||||
style: ['*'],
|
||||
summary: ['table'],
|
||||
// ---- Original: tabindex
|
||||
tabIndex: ['*'],
|
||||
target: ['a', 'area', 'base', 'form'],
|
||||
title: ['*'],
|
||||
translate: ['*'],
|
||||
type: ['button', 'input', 'embed', 'object', 'ol', 'script', 'source', 'style', 'menu', 'link'],
|
||||
usemap: ['img', 'input', 'object'],
|
||||
value: ['button', 'data', 'input', 'li', 'meter', 'option', 'progress', 'param', 'text' /** extra for TextNode */],
|
||||
width: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'],
|
||||
wrap: ['textarea'],
|
||||
// --- ARIA attributes
|
||||
// Source: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes
|
||||
ariaAutocomplete: ['*'],
|
||||
ariaChecked: ['*'],
|
||||
ariaDisabled: ['*'],
|
||||
ariaErrorMessage: ['*'],
|
||||
ariaExpanded: ['*'],
|
||||
ariaHasPopup: ['*'],
|
||||
ariaHidden: ['*'],
|
||||
ariaInvalid: ['*'],
|
||||
ariaLabel: ['*'],
|
||||
ariaLevel: ['*'],
|
||||
ariaModal: ['*'],
|
||||
ariaMultiline: ['*'],
|
||||
ariaMultiSelectable: ['*'],
|
||||
ariaOrientation: ['*'],
|
||||
ariaPlaceholder: ['*'],
|
||||
ariaPressed: ['*'],
|
||||
ariaReadonly: ['*'],
|
||||
ariaRequired: ['*'],
|
||||
ariaSelected: ['*'],
|
||||
ariaSort: ['*'],
|
||||
ariaValuemax: ['*'],
|
||||
ariaValuemin: ['*'],
|
||||
ariaValueNow: ['*'],
|
||||
ariaValueText: ['*'],
|
||||
ariaBusy: ['*'],
|
||||
ariaLive: ['*'],
|
||||
ariaRelevant: ['*'],
|
||||
ariaAtomic: ['*'],
|
||||
ariaDropEffect: ['*'],
|
||||
ariaGrabbed: ['*'],
|
||||
ariaActiveDescendant: ['*'],
|
||||
ariaColCount: ['*'],
|
||||
ariaColIndex: ['*'],
|
||||
ariaColSpan: ['*'],
|
||||
ariaControls: ['*'],
|
||||
ariaDescribedBy: ['*'],
|
||||
ariaDescription: ['*'],
|
||||
ariaDetails: ['*'],
|
||||
ariaFlowTo: ['*'],
|
||||
ariaLabelledBy: ['*'],
|
||||
ariaOwns: ['*'],
|
||||
ariaPosInset: ['*'],
|
||||
ariaRowCount: ['*'],
|
||||
ariaRowIndex: ['*'],
|
||||
ariaRowSpan: ['*'],
|
||||
ariaSetSize: ['*'],
|
||||
};
|
|
@ -0,0 +1,2 @@
|
|||
declare module '@babel/plugin-syntax-do-expressions';
|
||||
declare module '@babel/plugin-syntax-decorators';
|
|
@ -0,0 +1,19 @@
|
|||
import syntaxDecorators from '@babel/plugin-syntax-decorators';
|
||||
import dlight from './plugin';
|
||||
import { type DLightOption } from './types';
|
||||
import { type ConfigAPI, type TransformOptions } from '@babel/core';
|
||||
import { plugin as fn2Class } from '@inula/class-transformer';
|
||||
|
||||
export default function (_: ConfigAPI, options: DLightOption): TransformOptions {
|
||||
return {
|
||||
plugins: [
|
||||
['@babel/plugin-syntax-jsx'],
|
||||
['@babel/plugin-syntax-typescript', { isTSX: true }],
|
||||
[syntaxDecorators.default ?? syntaxDecorators, { legacy: true }],
|
||||
fn2Class,
|
||||
[dlight, options],
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export { type DLightOption };
|
|
@ -0,0 +1,43 @@
|
|||
import type babel from '@babel/core';
|
||||
import { type PluginObj } from '@babel/core';
|
||||
import { PluginProviderClass } from './pluginProvider';
|
||||
import { type DLightOption } from './types';
|
||||
import { defaultAttributeMap } from './const';
|
||||
|
||||
export default function (api: typeof babel, options: DLightOption): PluginObj {
|
||||
const { types } = api;
|
||||
const {
|
||||
files = '**/*.{js,ts,jsx,tsx}',
|
||||
excludeFiles = '**/{dist,node_modules,lib}/*',
|
||||
enableDevTools = false,
|
||||
htmlTags = defaultHtmlTags => defaultHtmlTags,
|
||||
attributeMap = defaultAttributeMap,
|
||||
} = options;
|
||||
|
||||
const pluginProvider = new PluginProviderClass(
|
||||
api,
|
||||
types,
|
||||
Array.isArray(files) ? files : [files],
|
||||
Array.isArray(excludeFiles) ? excludeFiles : [excludeFiles],
|
||||
enableDevTools,
|
||||
htmlTags,
|
||||
attributeMap
|
||||
);
|
||||
|
||||
return {
|
||||
visitor: {
|
||||
Program: {
|
||||
enter(path, { filename }) {
|
||||
return pluginProvider.programEnterVisitor(path, filename);
|
||||
},
|
||||
exit: pluginProvider.programExitVisitor.bind(pluginProvider),
|
||||
},
|
||||
ClassDeclaration: {
|
||||
enter: pluginProvider.classEnter.bind(pluginProvider),
|
||||
exit: pluginProvider.classExit.bind(pluginProvider),
|
||||
},
|
||||
ClassMethod: pluginProvider.classMethodVisitor.bind(pluginProvider),
|
||||
ClassProperty: pluginProvider.classPropertyVisitor.bind(pluginProvider),
|
||||
},
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,53 @@
|
|||
import { type types as t } from '@babel/core';
|
||||
|
||||
export type HTMLTags = string[] | ((defaultHtmlTags: string[]) => string[]);
|
||||
export interface DLightOption {
|
||||
/**
|
||||
* Files that will be included
|
||||
* @default ** /*.{js,jsx,ts,tsx}
|
||||
*/
|
||||
files?: string | string[];
|
||||
/**
|
||||
* Files that will be excludes
|
||||
* @default ** /{dist,node_modules,lib}/*.{js,ts}
|
||||
*/
|
||||
excludeFiles?: string | string[];
|
||||
/**
|
||||
* Enable devtools
|
||||
* @default false
|
||||
*/
|
||||
enableDevTools?: boolean;
|
||||
/**
|
||||
* Custom HTML tags.
|
||||
* Accepts 2 types:
|
||||
* 1. string[], e.g. ["div", "span"]
|
||||
* if contains "*", then all default tags will be included
|
||||
* 2. (defaultHtmlTags: string[]) => string[]
|
||||
* @default defaultHtmlTags => defaultHtmlTags
|
||||
*/
|
||||
htmlTags?: HTMLTags;
|
||||
/**
|
||||
* Allowed HTML tags from attributes
|
||||
* e.g. { alt: ["area", "img", "input"] }
|
||||
*/
|
||||
attributeMap?: Record<string, string[]>;
|
||||
}
|
||||
|
||||
export type PropertyContainer = Record<
|
||||
string,
|
||||
{
|
||||
node: t.ClassProperty | t.ClassMethod;
|
||||
deps: string[];
|
||||
isStatic?: boolean;
|
||||
isContent?: boolean;
|
||||
isChildren?: boolean | number;
|
||||
isModel?: boolean;
|
||||
isWatcher?: boolean;
|
||||
isPropOrEnv?: 'Prop' | 'Env';
|
||||
depsNode?: t.ArrayExpression;
|
||||
}
|
||||
>;
|
||||
|
||||
export type IdentifierToDepNode = t.SpreadElement | t.Expression;
|
||||
|
||||
export type SnippetPropSubDepMap = Record<string, Record<string, string[]>>;
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.
|
||||
* 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 { describe, expect, it } from 'vitest';
|
||||
import { transform } from './presets';
|
||||
|
||||
describe('condition', () => {
|
||||
it('should transform jsx', () => {
|
||||
expect(
|
||||
transform(`
|
||||
function App() {
|
||||
return <div>
|
||||
<if cond={count > 1}>{count} is bigger than is 1</if>
|
||||
<else>{count} is smaller than 1</else>
|
||||
</div>;
|
||||
}
|
||||
`)
|
||||
).toMatchInlineSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,233 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import { transform } from './presets';
|
||||
|
||||
describe('fn2Class', () => {
|
||||
it('should transform jsx', () => {
|
||||
expect(
|
||||
transform(`
|
||||
@View
|
||||
class A {
|
||||
Body() {
|
||||
return <div></div>
|
||||
}
|
||||
}`)
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { createElement as $$createElement, setStyle as $$setStyle, setDataset as $$setDataset, setEvent as $$setEvent, delegateEvent as $$delegateEvent, setHTMLProp as $$setHTMLProp, setHTMLAttr as $$setHTMLAttr, setHTMLProps as $$setHTMLProps, setHTMLAttrs as $$setHTMLAttrs, createTextNode as $$createTextNode, updateText as $$updateText, insertNode as $$insertNode, ForNode as $$ForNode, CondNode as $$CondNode, ExpNode as $$ExpNode, EnvNode as $$EnvNode, TryNode as $$TryNode, SnippetNode as $$SnippetNode, PropView as $$PropView, render as $$render } from "@dlightjs/dlight";
|
||||
class A extends View {
|
||||
Body() {
|
||||
let $node0;
|
||||
$node0 = $$createElement("div");
|
||||
return [$node0];
|
||||
}
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should transform jsx with reactive', () => {
|
||||
expect(
|
||||
transform(`
|
||||
@Main
|
||||
@View
|
||||
class A {
|
||||
count = 1
|
||||
Body() {
|
||||
return <div onClick={() => this.count++}>{this.count}</div>
|
||||
}
|
||||
}`)
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { createElement as $$createElement, setStyle as $$setStyle, setDataset as $$setDataset, setEvent as $$setEvent, delegateEvent as $$delegateEvent, setHTMLProp as $$setHTMLProp, setHTMLAttr as $$setHTMLAttr, setHTMLProps as $$setHTMLProps, setHTMLAttrs as $$setHTMLAttrs, createTextNode as $$createTextNode, updateText as $$updateText, insertNode as $$insertNode, ForNode as $$ForNode, CondNode as $$CondNode, ExpNode as $$ExpNode, EnvNode as $$EnvNode, TryNode as $$TryNode, SnippetNode as $$SnippetNode, PropView as $$PropView, render as $$render } from "@dlightjs/dlight";
|
||||
class A extends View {
|
||||
count = 1;
|
||||
$$count = 1;
|
||||
Body() {
|
||||
let $node0, $node1;
|
||||
this._$update = $changed => {
|
||||
if ($changed & 1) {
|
||||
$node1 && $node1.update(() => this.count, [this.count]);
|
||||
}
|
||||
};
|
||||
$node0 = $$createElement("div");
|
||||
$$delegateEvent($node0, "click", () => this._$ud(this.count++, "count"));
|
||||
$node1 = new $$ExpNode(this.count, [this.count]);
|
||||
$$insertNode($node0, $node1, 0);
|
||||
$node0._$nodes = [$node1];
|
||||
return [$node0];
|
||||
}
|
||||
}
|
||||
$$render("main", A);"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should transform fragment', () => {
|
||||
expect(
|
||||
transform(`
|
||||
@View
|
||||
class A {
|
||||
Body() {
|
||||
return <>
|
||||
<div></div>
|
||||
</>
|
||||
}
|
||||
}`)
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { createElement as $$createElement, setStyle as $$setStyle, setDataset as $$setDataset, setEvent as $$setEvent, delegateEvent as $$delegateEvent, setHTMLProp as $$setHTMLProp, setHTMLAttr as $$setHTMLAttr, setHTMLProps as $$setHTMLProps, setHTMLAttrs as $$setHTMLAttrs, createTextNode as $$createTextNode, updateText as $$updateText, insertNode as $$insertNode, ForNode as $$ForNode, CondNode as $$CondNode, ExpNode as $$ExpNode, EnvNode as $$EnvNode, TryNode as $$TryNode, SnippetNode as $$SnippetNode, PropView as $$PropView, render as $$render } from "@dlightjs/dlight";
|
||||
class A extends View {
|
||||
Body() {
|
||||
let $node0;
|
||||
$node0 = $$createElement("div");
|
||||
return [$node0];
|
||||
}
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should transform function component', () => {
|
||||
expect(
|
||||
transform(`
|
||||
function MyApp() {
|
||||
let count = 0;
|
||||
return <div onClick={() => count++}>{count}</div>
|
||||
}`)
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { createElement as $$createElement, setStyle as $$setStyle, setDataset as $$setDataset, setEvent as $$setEvent, delegateEvent as $$delegateEvent, setHTMLProp as $$setHTMLProp, setHTMLAttr as $$setHTMLAttr, setHTMLProps as $$setHTMLProps, setHTMLAttrs as $$setHTMLAttrs, createTextNode as $$createTextNode, updateText as $$updateText, insertNode as $$insertNode, ForNode as $$ForNode, CondNode as $$CondNode, ExpNode as $$ExpNode, EnvNode as $$EnvNode, TryNode as $$TryNode, SnippetNode as $$SnippetNode, PropView as $$PropView, render as $$render } from "@dlightjs/dlight";
|
||||
class MyApp extends View {
|
||||
count = 0;
|
||||
$$count = 1;
|
||||
Body() {
|
||||
let $node0, $node1;
|
||||
this._$update = $changed => {
|
||||
if ($changed & 1) {
|
||||
$node1 && $node1.update(() => this.count, [this.count]);
|
||||
}
|
||||
};
|
||||
$node0 = $$createElement("div");
|
||||
$$delegateEvent($node0, "click", () => this._$ud(this.count++, "count"));
|
||||
$node1 = new $$ExpNode(this.count, [this.count]);
|
||||
$$insertNode($node0, $node1, 0);
|
||||
$node0._$nodes = [$node1];
|
||||
return [$node0];
|
||||
}
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should transform function component reactively', () => {
|
||||
expect(
|
||||
transform(`
|
||||
function MyComp() {
|
||||
let count = 0
|
||||
return <>
|
||||
<h1>Hello dlight fn, {count}</h1>
|
||||
<button onClick={() => count +=1}>Add</button>
|
||||
<Button />
|
||||
</>
|
||||
}`)
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { createElement as $$createElement, setStyle as $$setStyle, setDataset as $$setDataset, setEvent as $$setEvent, delegateEvent as $$delegateEvent, setHTMLProp as $$setHTMLProp, setHTMLAttr as $$setHTMLAttr, setHTMLProps as $$setHTMLProps, setHTMLAttrs as $$setHTMLAttrs, createTextNode as $$createTextNode, updateText as $$updateText, insertNode as $$insertNode, ForNode as $$ForNode, CondNode as $$CondNode, ExpNode as $$ExpNode, EnvNode as $$EnvNode, TryNode as $$TryNode, SnippetNode as $$SnippetNode, PropView as $$PropView, render as $$render } from "@dlightjs/dlight";
|
||||
class MyComp extends View {
|
||||
count = 0;
|
||||
$$count = 1;
|
||||
Body() {
|
||||
let $node0, $node1, $node2, $node3, $node4;
|
||||
this._$update = $changed => {
|
||||
if ($changed & 1) {
|
||||
$node2 && $node2.update(() => this.count, [this.count]);
|
||||
}
|
||||
};
|
||||
$node0 = $$createElement("h1");
|
||||
$node1 = $$createTextNode("Hello dlight fn, ", []);
|
||||
$$insertNode($node0, $node1, 0);
|
||||
$node2 = new $$ExpNode(this.count, [this.count]);
|
||||
$$insertNode($node0, $node2, 1);
|
||||
$node0._$nodes = [$node1, $node2];
|
||||
$node3 = $$createElement("button");
|
||||
$$delegateEvent($node3, "click", () => this._$ud(this.count += 1, "count"));
|
||||
$node3.textContent = "Add";
|
||||
$node4 = new Button();
|
||||
$node4._$init(null, null, null, null);
|
||||
return [$node0, $node3, $node4];
|
||||
}
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should transform children props', () => {
|
||||
expect(
|
||||
transform(`
|
||||
function App({ children}) {
|
||||
return <h1>{children}</h1>
|
||||
}
|
||||
`)
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { createElement as $$createElement, setStyle as $$setStyle, setDataset as $$setDataset, setEvent as $$setEvent, delegateEvent as $$delegateEvent, setHTMLProp as $$setHTMLProp, setHTMLAttr as $$setHTMLAttr, setHTMLProps as $$setHTMLProps, setHTMLAttrs as $$setHTMLAttrs, createTextNode as $$createTextNode, updateText as $$updateText, insertNode as $$insertNode, ForNode as $$ForNode, CondNode as $$CondNode, ExpNode as $$ExpNode, EnvNode as $$EnvNode, TryNode as $$TryNode, SnippetNode as $$SnippetNode, PropView as $$PropView, render as $$render } from "@dlightjs/dlight";
|
||||
class App extends View {
|
||||
get children() {
|
||||
return this._$children;
|
||||
}
|
||||
Body() {
|
||||
let $node0, $node1;
|
||||
$node0 = $$createElement("h1");
|
||||
$node1 = new $$ExpNode(this.children, []);
|
||||
$$insertNode($node0, $node1, 0);
|
||||
$node0._$nodes = [$node1];
|
||||
return [$node0];
|
||||
}
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should transform component composition', () => {
|
||||
expect(
|
||||
transform(`
|
||||
function ArrayModification({name}) {
|
||||
let arr = 1
|
||||
return <section>
|
||||
<div>{arr}</div>
|
||||
</section>
|
||||
}
|
||||
|
||||
function MyComp() {
|
||||
return <>
|
||||
<ArrayModification name="1" />
|
||||
</>
|
||||
}
|
||||
`)
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { createElement as $$createElement, setStyle as $$setStyle, setDataset as $$setDataset, setEvent as $$setEvent, delegateEvent as $$delegateEvent, setHTMLProp as $$setHTMLProp, setHTMLAttr as $$setHTMLAttr, setHTMLProps as $$setHTMLProps, setHTMLAttrs as $$setHTMLAttrs, createTextNode as $$createTextNode, updateText as $$updateText, insertNode as $$insertNode, ForNode as $$ForNode, CondNode as $$CondNode, ExpNode as $$ExpNode, EnvNode as $$EnvNode, TryNode as $$TryNode, SnippetNode as $$SnippetNode, PropView as $$PropView, render as $$render } from "@dlightjs/dlight";
|
||||
class ArrayModification extends View {
|
||||
$p$name;
|
||||
name;
|
||||
arr = 1;
|
||||
$$arr = 2;
|
||||
Body() {
|
||||
let $node0, $node1, $node2;
|
||||
this._$update = $changed => {
|
||||
if ($changed & 2) {
|
||||
$node2 && $node2.update(() => this.arr, [this.arr]);
|
||||
}
|
||||
};
|
||||
$node0 = ArrayModification.$t0.cloneNode(true);
|
||||
$node1 = $node0.firstChild;
|
||||
$node2 = new $$ExpNode(this.arr, [this.arr]);
|
||||
$$insertNode($node1, $node2, 0);
|
||||
return [$node0];
|
||||
}
|
||||
static $t0 = (() => {
|
||||
let $node0, $node1;
|
||||
$node0 = $$createElement("section");
|
||||
$node1 = $$createElement("div");
|
||||
$node0.appendChild($node1);
|
||||
return $node0;
|
||||
})();
|
||||
}
|
||||
class MyComp extends View {
|
||||
Body() {
|
||||
let $node0;
|
||||
$node0 = new ArrayModification();
|
||||
$node0._$init([["name", "1", []]], null, null, null);
|
||||
return [$node0];
|
||||
}
|
||||
}"
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.
|
||||
* 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 plugin from '../dist';
|
||||
import { transform as transformWithBabel } from '@babel/core';
|
||||
|
||||
export function transform(code: string) {
|
||||
return transformWithBabel(code, {
|
||||
presets: [plugin],
|
||||
filename: 'test.tsx',
|
||||
})?.code;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"moduleResolution": "Node",
|
||||
"strict": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"ts-node": {
|
||||
"esm": true
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue