!158 feat(transpiler): add packageName options
* fix: transpiler * feat(transpiler): add packageName options * chore: remove dist
This commit is contained in:
parent
553613bbc6
commit
2640177de5
|
@ -4,5 +4,7 @@
|
||||||
package-lock.json
|
package-lock.json
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
/packages/**/node_modules
|
/packages/**/node_modules
|
||||||
dist
|
# to ignore all dist folders in all levels
|
||||||
|
**/dist
|
||||||
.history
|
.history
|
||||||
|
dumped*
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vitest --ui",
|
"test": "vitest --ui",
|
||||||
"bench": "vitest bench",
|
"bench": "vitest bench",
|
||||||
"dump-test": "babel tests --out-dir dumped-tests --extensions .tsx --presets=babel-preset-inula-jsx --copy-files --out-file-extension .ts"
|
"dump-test": "babel tests --out-dir dumped-tests --extensions .ts,.tsx --presets babel-preset-inula-jsx --copy-files --out-file-extension .ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/cli": "^7.23.9",
|
"@babel/cli": "^7.23.9",
|
||||||
|
|
|
@ -26,7 +26,7 @@ export default defineConfig({
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
// @ts-expect-error TODO: fix vite plugin interface is not compatible
|
// @ts-expect-error TODO: fix vite plugin interface is not compatible
|
||||||
inula(),
|
inula({ packageName: 'inula-reactive' }),
|
||||||
],
|
],
|
||||||
test: {
|
test: {
|
||||||
environment: 'jsdom', // or 'jsdom', 'node'
|
environment: 'jsdom', // or 'jsdom', 'node'
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { attributeMap, htmlTags, importMap } from './const';
|
||||||
|
|
||||||
|
|
||||||
export class PluginProvider {
|
export class PluginProvider {
|
||||||
private static readonly inulaPackageName = 'inula-reactive';
|
private inulaPackageName = 'inula-reactive';
|
||||||
// ---- Plugin Level ----
|
// ---- Plugin Level ----
|
||||||
private readonly babelApi: typeof babel
|
private readonly babelApi: typeof babel
|
||||||
private readonly t: typeof t
|
private readonly t: typeof t
|
||||||
|
@ -34,6 +34,7 @@ export class PluginProvider {
|
||||||
this.htmlTags = options.htmlTags ?? htmlTags;
|
this.htmlTags = options.htmlTags ?? htmlTags;
|
||||||
this.parseTemplate = options.parseTemplate ?? true;
|
this.parseTemplate = options.parseTemplate ?? true;
|
||||||
this.attributeMap = options.attributeMap ?? attributeMap;
|
this.attributeMap = options.attributeMap ?? attributeMap;
|
||||||
|
options.packageName && (this.inulaPackageName = options.packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Two levels of enter:
|
// ---- Two levels of enter:
|
||||||
|
@ -56,7 +57,7 @@ export class PluginProvider {
|
||||||
this.t.isImportDeclaration(n)
|
this.t.isImportDeclaration(n)
|
||||||
) as t.ImportDeclaration[];
|
) as t.ImportDeclaration[];
|
||||||
const inulaImports = this.allImports.filter(
|
const inulaImports = this.allImports.filter(
|
||||||
n => n.source.value === PluginProvider.inulaPackageName
|
n => n.source.value === this.inulaPackageName
|
||||||
);
|
);
|
||||||
// ---- @Enter2
|
// ---- @Enter2
|
||||||
if (inulaImports.length === 0) {
|
if (inulaImports.length === 0) {
|
||||||
|
|
|
@ -1,22 +1,20 @@
|
||||||
import babel, { transform, types as t, parseSync } from '@babel/core';
|
import babel, { transform, types as t, parseSync } from '@babel/core';
|
||||||
import inula from '../';
|
import inula from '../';
|
||||||
import babelJSX from '@babel/plugin-syntax-jsx';
|
import babelJSX from '@babel/plugin-syntax-jsx';
|
||||||
import generate from '@babel/generator';
|
import generate from '@babel/generator';
|
||||||
import { expect as ep } from 'vitest';
|
import { expect as ep } from 'vitest';
|
||||||
|
|
||||||
function formatCode(code: string) {
|
function formatCode(code: string) {
|
||||||
return generate(
|
return generate(parseSync(code, { plugins: [babelJSX] })!)!.code;
|
||||||
parseSync(code, {plugins: [babelJSX]})!
|
|
||||||
)!.code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transformInula(code: string) {
|
export function transformInula(code: string) {
|
||||||
return transform(code, {
|
return transform(code, {
|
||||||
presets: [[inula, {'files': '*'}]]
|
presets: [[inula, { files: '*', packageName: 'inula' }]],
|
||||||
})?.code;
|
})?.code;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function expect(ori: string, expected: string) {
|
export function expect(ori: string, expected: string) {
|
||||||
const transformed = transformInula(ori)!;
|
const transformed = transformInula(ori)!;
|
||||||
ep(formatCode(transformed)).toBe(formatCode(expected));
|
ep(formatCode(transformed)).toBe(formatCode(expected));
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,4 +27,9 @@ export interface InulaOption {
|
||||||
* @example { createElement: 'createElement' }
|
* @example { createElement: 'createElement' }
|
||||||
*/
|
*/
|
||||||
parseTemplate?: boolean
|
parseTemplate?: boolean
|
||||||
|
/**
|
||||||
|
* @brief The package name of inula. It will be used to identify whether we should transform the file.
|
||||||
|
* @default inula
|
||||||
|
*/
|
||||||
|
packageName?: string
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,217 +0,0 @@
|
||||||
import { ViewUnit, UnitProp } from '@inula/jsx-view-parser';
|
|
||||||
import Babel, { types, traverse } from '@babel/core';
|
|
||||||
|
|
||||||
interface ViewGeneratorConfig {
|
|
||||||
babelApi: typeof Babel;
|
|
||||||
importMap: Record<string, string>;
|
|
||||||
/**
|
|
||||||
* @brief Using AttributeMap to identify propertyfied attributes
|
|
||||||
* Reason for adding this:
|
|
||||||
* `el.prop = xxx` is faster than `el.setAttribute('prop', xxx)`
|
|
||||||
* @example { href: ["a", "area", "base", "link"], id: ["*"] }
|
|
||||||
*/
|
|
||||||
attributeMap?: Record<string, string[]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class BaseGenerator {
|
|
||||||
readonly viewUnit: ViewUnit;
|
|
||||||
readonly config: ViewGeneratorConfig;
|
|
||||||
readonly t: typeof types;
|
|
||||||
readonly traverse: typeof traverse;
|
|
||||||
readonly elementAttributeMap: Record<string, string[]>;
|
|
||||||
readonly importMap: Record<string, string>;
|
|
||||||
constructor(viewUnit: ViewUnit, config: ViewGeneratorConfig);
|
|
||||||
private readonly initStatements;
|
|
||||||
addStatement(...statements: types.Statement[]): void;
|
|
||||||
private readonly templates;
|
|
||||||
addTemplate(...template: types.Statement[]): void;
|
|
||||||
/**
|
|
||||||
* @brief To be implemented by the subclass
|
|
||||||
*/
|
|
||||||
run(): string;
|
|
||||||
generate(): [string, types.Statement[], types.Statement[]];
|
|
||||||
/**
|
|
||||||
* @brief Check if the expression is reactive, which satisfies the following conditions:
|
|
||||||
* 1. Contains .get() property
|
|
||||||
* 2. The whole expression is not a function
|
|
||||||
* @param expression
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
checkReactive(expression: types.Expression): boolean;
|
|
||||||
private readonly prefixMap;
|
|
||||||
nodeIdx: number;
|
|
||||||
geneNodeName(idx?: number): string;
|
|
||||||
templateIdx: number;
|
|
||||||
generateTemplateName(): string;
|
|
||||||
/**
|
|
||||||
* @brief Wrap the value in a file
|
|
||||||
* @param node
|
|
||||||
* @returns wrapped value
|
|
||||||
*/
|
|
||||||
wrapWithFile(node: types.Expression): types.File;
|
|
||||||
addWatch(value: types.Expression): types.CallExpression;
|
|
||||||
createCollector(): [types.Statement[], (statement: types.Statement | types.Statement[] | null) => void];
|
|
||||||
parseViewProp(prop: UnitProp, generateView: (units: ViewUnit[], config: ViewGeneratorConfig, templateIdx: number) => [types.Statement[], types.ExpressionStatement]): types.Expression;
|
|
||||||
parseProps(props: Record<string, UnitProp>, generateView: (units: ViewUnit[], config: ViewGeneratorConfig) => [types.Statement[], types.ExpressionStatement]): {
|
|
||||||
[k: string]: types.Expression;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class CompGenerator extends BaseGenerator {
|
|
||||||
run(): string;
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* const $el = createComponent(tag, {
|
|
||||||
* ...props
|
|
||||||
* }, spreadProps)
|
|
||||||
*/
|
|
||||||
declareCompNode(tag: types.Expression, props: Record<string, types.Expression>, children: ViewUnit[]): [string, types.Statement];
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class HTMLPropGenerator extends BaseGenerator {
|
|
||||||
static DelegatedEvents: Set<string>;
|
|
||||||
addHTMLProp(nodeName: string, tag: string, key: string, value: types.Expression): types.Statement;
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* setStyle($node, value)
|
|
||||||
*/
|
|
||||||
private setStyle;
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* setDataset($node, value)
|
|
||||||
*/
|
|
||||||
private setDataset;
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* $node.key = value
|
|
||||||
*/
|
|
||||||
private setStaticProperty;
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* setProperty($node, key, value)
|
|
||||||
*/
|
|
||||||
private setDynamicProperty;
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* $node.setAttribute(key, value)
|
|
||||||
*/
|
|
||||||
private setStaticAttribute;
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* setAttribute($node, key, value)
|
|
||||||
*/
|
|
||||||
private setDynamicAttribute;
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* delegateEvent($node, eventName, value)
|
|
||||||
*/
|
|
||||||
private setDelegatedEvent;
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* $node.addEventListener(eventName, value)
|
|
||||||
*/
|
|
||||||
private setStaticEvent;
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* addEventListener($node, eventName, value)
|
|
||||||
*/
|
|
||||||
private setDynamicEvent;
|
|
||||||
private setDynamicHTMLProp;
|
|
||||||
/**
|
|
||||||
* @brief Check if the attribute is internal, i.e., can be accessed as js property
|
|
||||||
* @param tag
|
|
||||||
* @param attribute
|
|
||||||
* @returns true if the attribute is internal
|
|
||||||
*/
|
|
||||||
isInternalAttribute(tag: string, attribute: string): boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class HTMLGenerator extends HTMLPropGenerator {
|
|
||||||
run(): string;
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* const $el = createElement(tag)
|
|
||||||
*/
|
|
||||||
declareHTMLNode(tag: types.Expression): [string, types.Statement];
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* $insert($el, childNode)
|
|
||||||
*/
|
|
||||||
private insertChildNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class TemplateGenerator extends HTMLPropGenerator {
|
|
||||||
run(): string;
|
|
||||||
private generateTemplate;
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* const $el = template.cloneNode(true)
|
|
||||||
*/
|
|
||||||
private declareTemplateNode;
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* $insert($el, childNode)
|
|
||||||
*/
|
|
||||||
private insertChildNode;
|
|
||||||
/**
|
|
||||||
* @View
|
|
||||||
* ${dlNodeName}.firstChild
|
|
||||||
* or
|
|
||||||
* ${dlNodeName}.firstChild.nextSibling
|
|
||||||
* or
|
|
||||||
* ...
|
|
||||||
* ${dlNodeName}.childNodes[${num}]
|
|
||||||
*/
|
|
||||||
private insertElement;
|
|
||||||
/**
|
|
||||||
* @brief Insert elements to the template node from the paths
|
|
||||||
* @param paths
|
|
||||||
* @param dlNodeName
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
private insertElements;
|
|
||||||
/**
|
|
||||||
* @brief Extract common prefix from paths
|
|
||||||
* e.g.
|
|
||||||
* [0, 1, 2, 3] + [0, 1, 2, 4] => [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 4]
|
|
||||||
* [0, 1, 2] is the common prefix
|
|
||||||
* @param paths
|
|
||||||
* @returns paths with common prefix
|
|
||||||
*/
|
|
||||||
private static pathWithCommonPrefix;
|
|
||||||
/**
|
|
||||||
* @brief Find the best node name and path for the given path by looking into the nameMap.
|
|
||||||
* If there's a full match, return the name and an empty path
|
|
||||||
* If there's a partly match, return the name and the remaining path
|
|
||||||
* If there's a nextSibling match, return the name and the remaining path with sibling offset
|
|
||||||
* @param nameMap
|
|
||||||
* @param path
|
|
||||||
* @param defaultName
|
|
||||||
* @returns [name, path, siblingOffset]
|
|
||||||
*/
|
|
||||||
private static findBestNodeAndPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class TextGenerator extends BaseGenerator {
|
|
||||||
run(): string;
|
|
||||||
declareTextNode(content: types.Literal): [string, types.Statement];
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class ExpressionGenerator extends BaseGenerator {
|
|
||||||
run(): string;
|
|
||||||
declareExpressionNode(expression: types.Expression): [string, types.Statement];
|
|
||||||
}
|
|
||||||
|
|
||||||
declare const viewGeneratorMap: {
|
|
||||||
readonly html: typeof HTMLGenerator;
|
|
||||||
readonly comp: typeof CompGenerator;
|
|
||||||
readonly template: typeof TemplateGenerator;
|
|
||||||
readonly text: typeof TextGenerator;
|
|
||||||
readonly exp: typeof ExpressionGenerator;
|
|
||||||
readonly if: typeof CompGenerator;
|
|
||||||
readonly env: typeof CompGenerator;
|
|
||||||
};
|
|
||||||
declare function generateNew(oldGenerator: any, viewUnit: ViewUnit, resetIdx?: boolean): [string, types.Statement[], types.Statement[]];
|
|
||||||
declare function generateView(viewUnits: ViewUnit[], config: ViewGeneratorConfig, templateIdx?: number): [types.Statement[], types.ExpressionStatement];
|
|
||||||
|
|
||||||
export { ViewGeneratorConfig, generateNew, generateView, viewGeneratorMap };
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -146,7 +146,7 @@ export class HTMLPropGenerator extends BaseGenerator {
|
||||||
value: t.Expression,
|
value: t.Expression,
|
||||||
) {
|
) {
|
||||||
return this.t.callExpression(
|
return this.t.callExpression(
|
||||||
this.t.identifier(this.importMap.delegateEvents),
|
this.t.identifier(this.importMap.delegateEvent),
|
||||||
[this.t.identifier(nodeName), this.t.stringLiteral(eventName), value]
|
[this.t.identifier(nodeName), this.t.stringLiteral(eventName), value]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
|
|
||||||
export const importMap = [
|
export const importMap = [
|
||||||
'createElement',
|
'createElement',
|
||||||
'setStyle',
|
'setStyle',
|
||||||
'setAttribute',
|
'setAttribute',
|
||||||
'setDataset',
|
'setDataset',
|
||||||
'setProperty',
|
'setProperty',
|
||||||
'setEvent',
|
'setEvent',
|
||||||
'delegateEvent',
|
'delegateEvent',
|
||||||
'addEventListener',
|
'addEventListener',
|
||||||
'watch',
|
'watch',
|
||||||
'insert',
|
'insert',
|
||||||
|
@ -510,4 +510,4 @@ export const attributeMap = {
|
||||||
ariaRowIndex: ['*'],
|
ariaRowIndex: ['*'],
|
||||||
ariaRowSpan: ['*'],
|
ariaRowSpan: ['*'],
|
||||||
ariaSetSize: ['*'],
|
ariaSetSize: ['*'],
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,73 +0,0 @@
|
||||||
import Babel, { types } from '@babel/core';
|
|
||||||
|
|
||||||
interface UnitProp {
|
|
||||||
value: types.Expression;
|
|
||||||
viewPropMap: Record<string, ViewUnit[]>;
|
|
||||||
specifier?: string;
|
|
||||||
}
|
|
||||||
interface TextUnit {
|
|
||||||
type: 'text';
|
|
||||||
content: types.Literal;
|
|
||||||
}
|
|
||||||
type MutableUnit = ViewUnit & {
|
|
||||||
path: number[];
|
|
||||||
};
|
|
||||||
interface TemplateProp {
|
|
||||||
tag: types.Expression;
|
|
||||||
name: string;
|
|
||||||
key: string;
|
|
||||||
path: number[];
|
|
||||||
value: types.Expression;
|
|
||||||
}
|
|
||||||
interface TemplateUnit {
|
|
||||||
type: 'template';
|
|
||||||
template: HTMLUnit;
|
|
||||||
mutableUnits: MutableUnit[];
|
|
||||||
props: TemplateProp[];
|
|
||||||
}
|
|
||||||
interface HTMLUnit {
|
|
||||||
type: 'html';
|
|
||||||
tag: types.Expression;
|
|
||||||
props: Record<string, UnitProp>;
|
|
||||||
children: ViewUnit[];
|
|
||||||
}
|
|
||||||
interface CompUnit {
|
|
||||||
type: 'comp';
|
|
||||||
tag: types.Expression;
|
|
||||||
props: Record<string, UnitProp>;
|
|
||||||
children: ViewUnit[];
|
|
||||||
}
|
|
||||||
interface IfBranch {
|
|
||||||
condition: types.Expression;
|
|
||||||
children: ViewUnit[];
|
|
||||||
}
|
|
||||||
interface IfUnit {
|
|
||||||
type: 'if';
|
|
||||||
branches: IfBranch[];
|
|
||||||
}
|
|
||||||
interface ExpUnit {
|
|
||||||
type: 'exp';
|
|
||||||
content: UnitProp;
|
|
||||||
}
|
|
||||||
interface EnvUnit {
|
|
||||||
type: 'env';
|
|
||||||
props: Record<string, UnitProp>;
|
|
||||||
children: ViewUnit[];
|
|
||||||
}
|
|
||||||
type ViewUnit = TextUnit | HTMLUnit | CompUnit | IfUnit | ExpUnit | EnvUnit | TemplateUnit;
|
|
||||||
interface ViewParserConfig {
|
|
||||||
babelApi: typeof Babel;
|
|
||||||
htmlTags: string[];
|
|
||||||
parseTemplate?: boolean;
|
|
||||||
}
|
|
||||||
type AllowedJSXNode = types.JSXElement | types.JSXFragment | types.JSXText | types.JSXExpressionContainer | types.JSXSpreadChild;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate view units from a babel ast
|
|
||||||
* @param statement
|
|
||||||
* @param config
|
|
||||||
* @returns ViewUnit[]
|
|
||||||
*/
|
|
||||||
declare function parseView(node: AllowedJSXNode, config: ViewParserConfig): ViewUnit[];
|
|
||||||
|
|
||||||
export { AllowedJSXNode, CompUnit, EnvUnit, ExpUnit, HTMLUnit, IfBranch, IfUnit, MutableUnit, TemplateProp, TemplateUnit, TextUnit, UnitProp, ViewParserConfig, ViewUnit, parseView };
|
|
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