modified: babel.config.js

modified:   example/App.tsx
	modified:   example/index.tsx
	modified:   index.ts
	modified:   package.json
	modified:   src/parser/Lexer.ts
	modified:   src/parser/mappingRule.ts
	modified:   src/parser/parseMappingRule.ts
	modified:   src/parser/parser.ts
	modified:   src/types/types.ts
	modified:   tsconfig.json
This commit is contained in:
wangyu 2023-12-06 15:37:37 +08:00
parent 925a6de0e2
commit fb3180f2f2
11 changed files with 97 additions and 90 deletions

View File

@ -13,7 +13,7 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
const {preset} = require("./jest.config"); const { preset } = require('./jest.config');
module.exports = { module.exports = {
presets: [ presets: [
[ [
@ -23,19 +23,15 @@ module.exports = {
browsers: ['> 1%', 'last 2 versions', 'not ie <= 8'], browsers: ['> 1%', 'last 2 versions', 'not ie <= 8'],
node: 'current', node: 'current',
}, },
useBuiltIns: 'usage',
corejs: 3,
}, },
], ],
['@babel/preset-typescript'],
[ [
'@babel/preset-typescript', '@babel/preset-react',
],
[
"@babel/preset-react",
{ {
"runtime": "automatic", runtime: 'automatic',
"importSource": "openinula" importSource: 'openinula',
} },
] ],
], ],
}; };

View File

@ -39,7 +39,7 @@ const App = () => {
<div className='container'> <div className='container'>
<Example1/> <Example1/>
<Example2/> <Example2/>
<Example3/> <Example3 locale={locale} setLocale={setLocale}/>
</div> </div>
<div className='container'> <div className='container'>
<Example4 locale={locale} messages={message}/> <Example4 locale={locale} messages={message}/>

View File

@ -17,9 +17,7 @@ import App from './App'
function render() { function render() {
Inula.render( Inula.render(
<> <App/>,
<App/>
</>,
document.querySelector('#root') as any document.querySelector('#root') as any
) )
} }

View File

@ -54,7 +54,7 @@ export default {
IntlProvider: I18nProvider, IntlProvider: I18nProvider,
injectIntl: injectIntl, injectIntl: injectIntl,
RawIntlProvider: InjectProvider, RawIntlProvider: InjectProvider,
} };
// 用于定义文本 // 用于定义文本
export function defineMessages<K extends keyof any, T = MessageDescriptor, U = Record<K, T>>(msgs: U): U { export function defineMessages<K extends keyof any, T = MessageDescriptor, U = Record<K, T>>(msgs: U): U {

View File

@ -4,10 +4,11 @@
"description": "", "description": "",
"main": "build/intl.umd.js", "main": "build/intl.umd.js",
"type": "commonjs", "type": "commonjs",
"types": "build/index.d.ts", "types": "build/@types/index.d.ts",
"scripts": { "scripts": {
"demo-serve": "webpack serve --mode=development", "demo-serve": "webpack serve --mode=development",
"build": "rollup --config rollup.config.js", "rollup-build": "rollup --config rollup.config.js && npm run build-types",
"build-types": "tsc -p tsconfig.json && rollup -c build-type.js",
"test": "jest --config jest.config.js", "test": "jest --config jest.config.js",
"test-c": "jest --coverage" "test-c": "jest --coverage"
}, },
@ -33,6 +34,7 @@
"@rollup/plugin-babel": "^6.0.3", "@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-node-resolve": "^7.1.3", "@rollup/plugin-node-resolve": "^7.1.3",
"@rollup/plugin-typescript": "^11.0.0", "@rollup/plugin-typescript": "^11.0.0",
"rollup-plugin-dts": "^6.1.0",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0", "@testing-library/react": "^14.0.0",
"@types/node": "^16.18.27", "@types/node": "^16.18.27",
@ -40,7 +42,6 @@
"babel": "^6.23.0", "babel": "^6.23.0",
"babel-jest": "^29.5.0", "babel-jest": "^29.5.0",
"babel-loader": "^9.1.2", "babel-loader": "^9.1.2",
"core-js": "3.31.0",
"html-webpack-plugin": "^5.5.1", "html-webpack-plugin": "^5.5.1",
"jest": "29.3.1", "jest": "29.3.1",
"jest-environment-jsdom": "^29.5.0", "jest-environment-jsdom": "^29.5.0",
@ -56,8 +57,6 @@
"typescript": "4.9.3", "typescript": "4.9.3",
"webpack": "^5.81.0", "webpack": "^5.81.0",
"webpack-cli": "^5.1.4", "webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.13.3", "webpack-dev-server": "^4.13.3"
"react": "18.2.0",
"react-dom": "18.2.0"
} }
} }

View File

@ -14,51 +14,45 @@
*/ */
import ruleUtils from '../utils/parseRuleUtils'; import ruleUtils from '../utils/parseRuleUtils';
import { LexerInterface } from "../types/interfaces"; import { LexerInterface } from '../types/interfaces';
const getMatch = ruleUtils.checkSticky()
? // 正则表达式具有 sticky 标志
(regexp, buffer) => regexp.exec(buffer)
: // 正则表达式具有 global 标志,匹配的字符串长度为 0则表示匹配失败
(regexp, buffer) => (regexp.exec(buffer)[0].length === 0 ? null : regexp.exec(buffer));
class Lexer<T> implements LexerInterface<T> { class Lexer<T> implements LexerInterface<T> {
readonly startState: string; readonly startState: string;
readonly states: Record<string, any>; readonly states: Record<string, any>;
private buffer: string = ''; private buffer: string = '';
private stack: string[] = []; private stack: string[] = [];
private index; private index: number = 0;
private line; private line: number = 1;
private col; private col: number = 1;
private queuedText; private queuedText: string = '';
private state; private state: string = '';
private groups; private groups: string[] = [];
private error; private error: Record<string, any> | undefined;
private regexp; private regexp;
private fast; private fast: object = {};
private queuedGroup; private queuedGroup: string | null = '';
private value; private value: string = '';
constructor(states, state) { constructor(unionReg: Record<string, any>, startState: string) {
this.startState = state; this.startState = startState;
this.states = states; this.states = unionReg;
this.buffer = ''; this.buffer = '';
this.stack = []; this.stack = [];
this.reset(); this.reset();
} }
public reset(data?, info?) { public reset(data?: string) {
this.buffer = data || ''; this.buffer = data || '';
this.index = 0; this.index = 0;
this.line = info ? info.line : 1; this.line = 1;
this.col = info ? info.col : 1; this.col = 1;
this.queuedText = info ? info.queuedText : ''; this.queuedText = '';
this.setState(info ? info.state : this.startState); this.setState(this.startState);
this.stack = info && info.stack ? info.stack.slice() : []; this.stack = [];
return this; return this;
} }
private setState(state) { private setState(state: string) {
if (!state || this.state === state) { if (!state || this.state === state) {
return; return;
} }
@ -71,15 +65,15 @@ class Lexer<T> implements LexerInterface<T> {
} }
private popState() { private popState() {
this.setState(this.stack.pop()); this.setState(<string>this.stack.pop());
} }
private pushState(state) { private pushState(state: string) {
this.stack.push(this.state); this.stack.push(this.state);
this.setState(state); this.setState(state);
} }
private getGroup(match) { private getGroup(match: Record<string, any>) {
const groupCount = this.groups.length; const groupCount = this.groups.length;
for (let i = 0; i < groupCount; i++) { for (let i = 0; i < groupCount; i++) {
if (match[i + 1] !== undefined) { if (match[i + 1] !== undefined) {
@ -127,7 +121,7 @@ class Lexer<T> implements LexerInterface<T> {
const group = this.getGroup(match); const group = this.getGroup(match);
const text = match[0]; const text = match[0];
if (error.fallback && match.index !== index) { if (error?.fallback && match.index !== index) {
this.queuedGroup = group; this.queuedGroup = group;
this.queuedText = text; this.queuedText = text;
return this.getToken(error, buffer.slice(index, match.index), index); return this.getToken(error, buffer.slice(index, match.index), index);
@ -136,7 +130,14 @@ class Lexer<T> implements LexerInterface<T> {
return this.getToken(group, text, index); return this.getToken(group, text, index);
} }
private getToken(group, text, offset) { /**
* Token
* @param group
* @param text
* @param offset
* @private
*/
private getToken(group: any, text: string, offset: number) {
let lineNum = 0; let lineNum = 0;
let last = 1; // 最后一个换行符的索引位置 let last = 1; // 最后一个换行符的索引位置
if (group.lineBreaks) { if (group.lineBreaks) {
@ -192,9 +193,14 @@ class Lexer<T> implements LexerInterface<T> {
next: (): IteratorResult<T> => { next: (): IteratorResult<T> => {
const token = this.next(); const token = this.next();
return { value: token, done: !token } as IteratorResult<T>; return { value: token, done: !token } as IteratorResult<T>;
} },
} };
} }
} }
const getMatch = ruleUtils.checkSticky()
? // 正则表达式具有 sticky 标志
(regexp, buffer) => regexp.exec(buffer)
: // 正则表达式具有 global 标志,匹配的字符串长度为 0则表示匹配失败
(regexp, buffer) => (regexp.exec(buffer)[0].length === 0 ? null : regexp.exec(buffer));
export default Lexer; export default Lexer;

View File

@ -70,5 +70,5 @@ const select: Record<string, any> = {
export const mappingRule: Record<string, any> = { export const mappingRule: Record<string, any> = {
body, body,
arg, arg,
select select,
}; };

View File

@ -16,16 +16,16 @@
import Lexer from './Lexer'; import Lexer from './Lexer';
import { mappingRule } from './mappingRule'; import { mappingRule } from './mappingRule';
import ruleUtils from '../utils/parseRuleUtils'; import ruleUtils from '../utils/parseRuleUtils';
import { RawToken } from "../types/types"; import { RawToken } from '../types/types';
const defaultErrorRule = ruleUtils.getRuleOptions('error', { lineBreaks: true, shouldThrow: true }); const defaultErrorRule = ruleUtils.getRuleOptions('error', { lineBreaks: true, shouldThrow: true });
// 解析规则并生成词法分析器所需的数据结构,以便进行词法分析操作 // 解析规则并生成词法分析器所需的数据结构,以便进行词法分析操作
function parseRules(rules: Record<string, any>, hasStates: boolean): Record<string, any> { function parseRules(rules: Record<string, any>, hasStates: boolean): Record<string, any> {
let errorRule: Record<string, any> | null = null; let errorRule: Record<string, any> | null = null;
const fast = {}; const fast: object = {};
let enableFast = true; let enableFast: boolean = true;
let unicodeFlag = null; let unicodeFlag: boolean | null = null;
const groups: Record<string, any>[] = []; const groups: Record<string, any>[] = [];
const parts: string[] = []; const parts: string[] = [];
@ -108,11 +108,11 @@ export function checkStateGroup(group: Record<string, any>, name: string, map: R
} }
// 将国际化解析规则注入分词器中 // 将国际化解析规则注入分词器中
function parseMappingRule(mappingRule: Record<string, any>, start?: string): Lexer<RawToken> { function parseMappingRule(mappingRule: Record<string, any>, startState?: string): Lexer<RawToken> {
const keys = Object.getOwnPropertyNames(mappingRule); const keys = Object.getOwnPropertyNames(mappingRule);
if (!start) { if (!startState) {
start = keys[0]; startState = keys[0];
} }
// 将每个状态的规则解析为规则数组,并存储在 ruleMap 对象中 // 将每个状态的规则解析为规则数组,并存储在 ruleMap 对象中
@ -153,27 +153,27 @@ function parseMappingRule(mappingRule: Record<string, any>, start?: string): Lex
} }
} }
const map = {}; const mappingAllRules = {};
// 将规则映射为词法分析器数据结构,并存储在 map 对象中 // 将规则映射为词法分析器数据结构,并存储在 mappingAllRules 对象中
keys.forEach(key => { keys.forEach(key => {
map[key] = parseRules(ruleMap[key], true); mappingAllRules[key] = parseRules(ruleMap[key], true);
}); });
// 检查状态组中的规则是否正确引用了其他状态 // 检查状态组中的规则是否正确引用了其他状态
keys.forEach(name => { keys.forEach(name => {
const state = map[name]; const state = mappingAllRules[name];
const groups = state.groups; const groups = state.groups;
groups.forEach(group => { groups.forEach(group => {
checkStateGroup(group, name, map); checkStateGroup(group, name, mappingAllRules);
}); });
const fastKeys = Object.getOwnPropertyNames(state.fast); const fastKeys = Object.getOwnPropertyNames(state.fast);
fastKeys.forEach(fastKey => { fastKeys.forEach(fastKey => {
checkStateGroup(state.fast[fastKey], name, map); checkStateGroup(state.fast[fastKey], name, mappingAllRules);
}); });
}); });
return new Lexer(map, start); return new Lexer(mappingAllRules, startState);
} }
function processFast(match, fast: {}, options) { function processFast(match, fast: {}, options) {

View File

@ -17,7 +17,7 @@ import { lexer } from './parseMappingRule';
import { RawToken, Token } from '../types/types'; import { RawToken, Token } from '../types/types';
import { DEFAULT_PLURAL_KEYS } from '../constants'; import { DEFAULT_PLURAL_KEYS } from '../constants';
import { Content, FunctionArg, PlainArg, Select, TokenContext } from '../types/interfaces'; import { Content, FunctionArg, PlainArg, Select, TokenContext } from '../types/interfaces';
import Lexer from "./Lexer"; import Lexer from './Lexer';
const getContext = (lt: Record<string, any>): TokenContext => ({ const getContext = (lt: Record<string, any>): TokenContext => ({
offset: lt.offset, offset: lt.offset,
@ -29,15 +29,14 @@ const getContext = (lt: Record<string, any>): TokenContext => ({
export const checkSelectType = (value: string): boolean => { export const checkSelectType = (value: string): boolean => {
return value === 'plural' || value === 'select' || value === 'selectordinal'; return value === 'plural' || value === 'select' || value === 'selectordinal';
} };
class Parser { class Parser {
lexer: Lexer<RawToken>;
cardinalKeys: string[] = DEFAULT_PLURAL_KEYS; cardinalKeys: string[] = DEFAULT_PLURAL_KEYS;
ordinalKeys: string[] = DEFAULT_PLURAL_KEYS; ordinalKeys: string[] = DEFAULT_PLURAL_KEYS;
constructor(message: string) { constructor(message: string) {
this.lexer = lexer.reset(message); lexer.reset(message);
} }
isSelectKeyValid(token: RawToken, type: Select['type'], value: string) { isSelectKeyValid(token: RawToken, type: Select['type'], value: string) {
@ -60,7 +59,7 @@ class Parser {
isPlural = true; isPlural = true;
} }
for (const token of this.lexer) { for (const token of lexer) {
switch (token.type) { switch (token.type) {
case 'offset': { case 'offset': {
if (type === 'select') { if (type === 'select') {
@ -97,7 +96,7 @@ class Parser {
parseToken(token: RawToken, isPlural: boolean): PlainArg | FunctionArg | Select { parseToken(token: RawToken, isPlural: boolean): PlainArg | FunctionArg | Select {
const context = getContext(token); const context = getContext(token);
const nextToken = this.lexer.next(); const nextToken = lexer.next();
if (!nextToken) { if (!nextToken) {
throw new Error('The message end position is invalid.'); throw new Error('The message end position is invalid.');
@ -111,7 +110,7 @@ class Parser {
return { type: 'argument', arg: token.value, ctx: context }; return { type: 'argument', arg: token.value, ctx: context };
} }
case 'func-simple': { case 'func-simple': {
const end = this.lexer.next(); const end = lexer.next();
if (!end) { if (!end) {
throw new Error('The message end position is invalid.'); throw new Error('The message end position is invalid.');
} }
@ -159,7 +158,7 @@ class Parser {
const tokens: any[] = []; const tokens: any[] = [];
let content: string | Content | null = null; let content: string | Content | null = null;
for (const token of this.lexer) { for (const token of lexer) {
if (token.type === 'argument') { if (token.type === 'argument') {
if (content) { if (content) {
content = null; content = null;
@ -175,7 +174,7 @@ class Parser {
} else if (token.type === 'doubleapos') { } else if (token.type === 'doubleapos') {
tokens.push(token.value); tokens.push(token.value);
} else if (token.type === 'quoted') { } else if (token.type === 'quoted') {
tokens.push(token.value) tokens.push(token.value);
} else if (token.type === 'content') { } else if (token.type === 'content') {
tokens.push(token.value); tokens.push(token.value);
} else { } else {

View File

@ -21,9 +21,10 @@ import {
Select, Select,
FunctionArg, FunctionArg,
I18nContextProps, I18nContextProps,
configProps configProps,
InjectedIntl,
} from './interfaces'; } from './interfaces';
import I18n from "../core/I18n"; import I18n from '../core/I18n';
export type Error = string | ((message, id, context) => string); export type Error = string | ((message, id, context) => string);
@ -71,11 +72,15 @@ export type RawToken = {
col: number; col: number;
}; };
export type I18nProviderProps = I18nContextProps & configProps export type I18nProviderProps = I18nContextProps & configProps;
export type IntlType = { export type IntlType = {
i18n: I18n; i18n: I18n;
formatMessage: Function, formatMessage: Function;
formatNumber: Function, formatNumber: Function;
formatDate: Function, formatDate: Function;
};
export interface InjectedIntlProps {
intl: InjectedIntl;
} }

View File

@ -31,6 +31,7 @@
"declaration": true, "declaration": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"downlevelIteration": true, "downlevelIteration": true,
"declarationDir": "./build/@types",
// 使@types/node // 使@types/node
"lib": [ "lib": [
"dom", "dom",
@ -53,19 +54,22 @@
} }
}, },
"include": [ "include": [
"./src/**/*", "./index.ts"
"./src/format/**/*.ts",
"./example/**/*"
], ],
"exclude": [ "exclude": [
"node_modules", "node_modules",
"lib", "lib",
"**/*.spec.ts", "**/*.spec.ts",
"dev" "dev",
"./example/**/*",
"./tsconfig.json"
], ],
"types": [ "types": [
"node", "node",
"jest", "jest",
"@testing-library/jest-dom" "@testing-library/jest-dom"
],
"files": [
"./index.ts"
] ]
} }