tjs-parser

1.4.0 • Public • Published

tjs-parser 编译工具

转译 .tjs.tjson.tjsx 文件为 .js 文件

使用方式

安装依赖

npm i tjs-parser --save-dev

or

yarn add tjs-parser --dev

引用方式

// 引入模块
const {
  parse
  parseFile
} = require('tjs-parser')


// 使用方法一:通过字符串编译
/**
 * @param {string} input - 代码字符串
 * @param {object} [option] - 编译参数 - 具体参数见下方说明
 * @return {object} result
 */
const result = parse(input, option)

// 使用方法二:通过文件路径编译
/**
 * @param {string} path - 代码文件路径
 * @param {object} [option] - 编译参数 - 具体参数见下方说明
 * @return {object} result
 */
const result = parseFile(path, option)
// 等同于 const result = parse(input, { ...option, path })

参数及返回说明

编译参数 - option

以下参数均为可选项

通用参数

/**
 * @param {string} [option.path] - 代码文件的路径; 用于识别代码内容模式及导出函数时的名称
 * @param {boolean} [option.sourceMap] - 是否生成 source-map
 * @param {number} [option.tabSize = 2] - 读取代码时的匹配为缩进的空格数; 默认为2个空格为一次缩进
 */

解析时参数

/**
 * @param {string} [option.commentCommandPrefix = '@tjs-set '] - 单行注释命令的匹配前缀;例如 //@tjs-set tabSize 1
 * @param {function} [option.emitWarning = console.wran] - 触发警告的方法
 * @param {object<string,function>} [option.stringBlockParsers] - 字符串块的处理函数对象; 三引号包裹的字符串块的处理方法对象; <tagName, parser>
 */

注释命令

注释命令格式形如 //@tjs-set key [value] 注释命令相当于是在解析时设置的参数,会覆盖全局参数 如果value未给出,且类型未boolean值时,该值为true;例如 //@tjs-set tjsxMode 等同于 //@tjs-set tjsxMode true 如果value是需要设置默认值时可以使用defalut关键字,例如 //@tjs-set tjsx default 如果value是字符串时,可以直接写字面量也可以使用双引号包裹,例如 //@tjs-set tjsx a 或者 //@tjs-set tjsx "a" 如果value是字符串数组时,必须使用方括号及双引号包裹,例如 //@tjs-set tjsonArguments ["a", "b"]

字符串块处理函数对象

字符串块用于书写多行文本,且可以指定一个 tagName 用于该字符串的处理

tjsx 语法参数

/**
 * @param {boolean} [option.tjsxReact = false] - tjsx 语法中,是否启用 react 模式
 * @param {string} [option.tjsx = 'createElement'] - tjsx 语法中用于创建节点时的方法名称; tjsxReact 为真时默认为 React.createElement, 为假时默认为 createElement
 * @param {string} [option.tjsxUndefinedValue = 'undefined'] - tjsx 语法中,缺省的节点属性值; tjsxReact 为真时默认为 true, 为假时默认为 undefined
 * @param {boolean} [option.tjsxCapitalLiteral = false] - tjsx 语法中,该值为真时大写首字母的 tagName 转为字面量,为假时转换为字符串; tjsxReact 为真时默认为 true, 为假时默认为 false
 * @param {boolean} [option.tjsxFlatChildren = false] - tjsx 语法中,是否展开子节点列表; 该值为真时子节点为参数列表,为假时为数组; tjsxReact 为真时默认为 true, 为假时默认为 false
 * @param {boolean} [option.tjsxReturnNullAttribute = false] - 创建节点时,没有子节点及属性时是否填充属性值的参数占位 null
 */

option.tjsxReact 为假时,即默认状态:

  • option.tjsxundefined 时会被设置为 'createElement'
  • option.tjsxFlatChildrenundefined 时会被设置为 false
  • option.tjsxCapitalLiteralundefined 时会被设置为 false
  • option.tjsxUndefinedValueundefined 时会被设置为 'undefined'

option.tjsxReact 为真时:

  • option.tjsxundefined 时会被设置为 'React.createElement'
  • option.tjsxFlatChildrenundefined 时会被设置为 true
  • option.tjsxCapitalLiteralundefined 时会被设置为 true
  • option.tjsxUndefinedValueundefined 时会被设置为 'true'

tjson 模式参数

这里的模式是指影响代码输出方式的意思

/**
 * @param {boolean} [option.tjsonMode] - 当前解析内容是否是 tjson 模式; 默认导出为单个对象
 * @param {string} [option.tjsonTarget = 'commonjs'] - tjson 模式下,导出模块类型。会自动增加导出字符串; 可选范围:['commonjs', 'module', 'var']
 * @param {string} [option.tjsonLibrary = '__output'] - tjson 模式下,导出变量名称; 在 tjsonTarget 为 var 时使用
 * @param {string} [option.tjsonType = 'auto'] - tjson 模式下,导出对象类型; 可选范围:[auto|array|object]
 */

option.tjsonMode 为真时:

  • option.tjsonArguments 被设置时会将内容导出为一个函数,并将参数作为形参

option.tjsonType 被设置时:

  • option.tjsonTypeauto 时,会识别代码是否为 object
  • option.tjsonTypeobject 或者 array 时,会将代码用 {} 或者 [] 包裹

tjsx 模式参数

这里的模式是指影响代码输出方式的意思

/**
 * @param {boolean} [option.tjsxMode = false] - 当前解析的文件内容是否是 tjsx 模式; 该设置会设置 tjsonMode 为 true
 * @param {array<string>} [option.tjsxArguments] - tjsx 模式下,导出函数的形参名称列表; 给出此选项会把输出包裹成一个箭头函数; tjsxReact 为真时默认为 ['props', 'ref', React = require('react)], 为假时默认为 ['createElement', 'props']
 * @param {boolean|string} [option.tjsxExportName] - tjsx 模式下,导出函数的名称; 此选项仅在 tjsxArguments 给出时生效; tjsxReact 为真时默认为文件名
 * @param {string} [option.tjsxReactMemo] - tjsx 模式下,导出函数是否被 react.memo() 方法包裹
 * @param {string} [option.tjsxReactForwardRef] - tjsx 模式下,导出函数是否被 react.forwardRef() 方法包裹
 * @param {boolean} [option.tjsxReturnRoot = false] - tjsx 模式下,该值为真时返回一个根节点,为假时返回一个子节点数组
 * @param {string} [option.tjsxRoot = 'tjsx-root'] - tjsx 模式下,tjsxReturnRoot 为真时,根节点的 tagName
 * @param {string} [option.tjsxPrefix] - tjsx 模式下,导出函数代码的前置包裹字符串
 * @param {string} [option.tjsxSuffix] - tjsx 模式下,导出函数代码的后置包裹字符串
 */

option.tjsxMode 为真时:

  • option.tjsonMode 会被设置为 true
  • option.tjsxReact 为假时,即默认状态:
    • option.tjsxReturnRootundefined 时会被设置为 false
    • option.tjsxRootundefined 时会被设置为 'tjsx-root'
    • option.tjsxArgumentsundefined 时会被设置为 ['createElement', 'props']
  • option.tjsxReact 为真时:
    • option.tjsxReturnRootundefined 时会被设置为 true
    • option.tjsxRootundefined 时会被设置为 'React.Fragment'
    • option.tjsxArgumentsundefined 时会被设置为 ['props', 'ref', 'React = require("react")']
    • option.tjsxExportNameundefined 时会被设置为文件名

格式化输出时参数

/**
 * @param {number} [option.outputBaseTabs] - 输出代码的基础缩进数
 * @param {string} [option.outputTabString] - 输出代码的缩进字符串
 * @param {string} [option.outputLnString] - 输出代码的换行字符串
 * @param {string|null} [option.outputPrefix] - 输出代码的前置包裹字符串
 * @param {string|null} [option.outputSuffix] - 输出代码的后置包裹字符串
 */

最终的 option.outputPrefixoption.outputSuffix 从外到内依次包含

  • option.outputPrefix - option.outputSuffix 包裹字符串
  • option.tjsonTarget 生成的导出声明语句包裹字符串
  • option.tjsxPrefix - option.tjsxSuffix 包裹字符串
  • option.tjsxReactForwardRefoption.tjsxReactMemo 生成包裹的调用声明语句
  • option.tjsxArguments 生成的函数声明语句包裹字符串

精简输出代码参数

/**
 * @param {boolean} [option.removeCodeless] - 输出代码时移除空白字符及注释
 * @param {boolean} [option.removeComment] - 输出代码时移除单行和多行注释
 * @param {boolean} [option.removeBlockComment] - 输出代码时移除多行注释
 * @param {boolean} [option.removeLineComment] - 输出代码时移除单行注释
 * @param {boolean} [option.removeBlank] - 输出代码时移除空白空格、缩进、换行符
 * @param {boolean} [option.removeSpace] - 输出代码时移除空格
 * @param {boolean} [option.removeDebugger] - 输出代码时移除 debugger 关键字
 */

option.removeCodeless 为真时:

  • option.removeComment 会被设置为 true
  • option.removeBlank 会被设置为 true
  • option.removeDebugger 会被设置为 true

option.removeComment 为真时:

  • option.removeBlockComment 会被设置为 true
  • option.removeLineComment 会被设置为 true

option.removeBlank 为真时:

  • option.removeSpace 会被设置为 true
  • option.outputTabString 会被设置为 ''
  • option.outputLnString 会被设置为 ''

返回对象 - result

/**
 * @return {string} result.code - 转换后的代码; getter 模式
 * @return {string} [result.map] - source-map 的 json 字符串; 仅在 option.sourceMap 为真时可用;getter 模式
 * @return {string} [result.inlineMap] - source-map 的 data-url 的引用字符串; 仅在 option.sourceMap 为真时可用;getter 模式
 * @return {object} [result.sourceMap] - source-map 对象; 仅在 option.sourceMap 为真时可用;
 * @return {object} result.tree - 转换后的代码行树对象
 * @return {object} result.option - 转换时的参数对象
 */

示例演示

编译 tjs 文件

模块文件 - input.tjs:

if a
  console.log(a)
else
  console.log(null)

const listEl = #ul
  #li
    `part 1`
  #li
    `part 2`
console.log(listEl)

使用模块文件 - user.tjs:

const { code } = parseFile('./input.tjs')

编译后的代码为 - code:

if (a) {
  console.log(a);
}
else {
  console.log(null);
}
const listEl = createElement("ul", null, [
  createElement("li", null, [ `part 1` ]),
  createElement("li", null, [ `part 2` ])
]);
console.log(listEl);

编译 tjson 文件

.tjson 文件默认会设置 option.tjsonMode = true

模块文件 - input.tjson:

name: 'demo'
version: '1.0.0'
author: 'mrbrick@yinhe.org'

使用模块文件 - user.tjs:

const { code } = parseFile('./input.tjson')

编译后的代码为 - code:

module.exports = {
  name: 'demo',
  version: '1.0.0',
  author: 'mrbrick@yinhe.org'
};

编译 tjsx 文件

.tjsx 文件默认会设置 option.tjsxMode = true

默认版本

模块文件 - input.tjsx:

option.tjsxReact 为假时页面会自动引入 createElement, props 两个全局变量 createElement 为创建元素的方法,props 为数据传递的变量, 可通过 option.tjsxArguments 进行自定义

const
  title = 'number list'
  number = 3
= props

#h2
  `section - ${title}`
#div
  #p
    "part 1"
  #p
    "part 2"
  #p
    "part 3"
#ul id="list" data-active
  for number as i
    #li data-index = i
      `${i + 1}`

propstjsx 模式下默认使用的数据传递对象

使用模块文件 - user.tjs:

const { code } = parseFile('./input.tjsx')

编译后的代码为 - code:

module.exports = (createElement, props = {}) => ((() => {
  const __arr = [];
  const {
    title = 'number list',
    number = 3
  } = props;
  __arr.push(createElement("h2", null, [ `section - ${title}` ]));
  __arr.push(createElement("div", null, [
    createElement("p", null, [ "part 1" ]),
    createElement("p", null, [ "part 2" ]),
    createElement("p", null, [ "part 3" ])
  ]));
  __arr.push(createElement("ul", { "id": "list", "data-active": undefined }, (() => {
    const __arr = [];
    for (let i = 0; i < number; i++) {
      __arr.push(createElement("li", { "data-index": i }, [ `${i + 1}` ]));
    }
    return __arr;
  })()));
  return __arr;
})());

react 版本

option.tjsxReact 为真时页面会自动引入 props, React 两个全局变量,所以需要先安装 react 的 npm 包

模块文件 - input.tjsx:

// 这里我们用注释命令开启设置,也可以在编译前设置 `option.tjsxReact = true`
//@tjs-set tjsxReact true

const
  title = 'number list'
  number = 3
= props

#h2
  `section - ${title}`
#div
  #p
    "part 1"
  #p
    "part 2"
  #p
    "part 3"
#ul id="list" data-active
  push for number as i
    #li data-index = i
      `${i + 1}`

文首的注释命令相当于在编译前设置 option.tjsxReact = true,也可以全局设置 propstjsxReact 模式下默认使用的数据传递对象 React 对象会默认通过 require 方法导入进来,可以直接使用

使用模块文件 - user.tjs:

const { code } = parseFile('./input.tjsx')

编译后的代码为 - code:

module.exports = (props, ref, React = require("react")) => (React.createElement(React.Fragment, null, ...(() => {
  const __arr = [];
  const {
    title = 'number list',
    number = 3
  } = props;
  __arr.push(React.createElement("h2", null, `section - ${title}`));
  __arr.push(React.createElement("div", null, ...[
    React.createElement("p", null, "part 1"),
    React.createElement("p", null, "part 2"),
    React.createElement("p", null, "part 3")
  ]));
  __arr.push(React.createElement("ul", { "id": "list", "data-active": true }, ...(() => {
    const __arr = [];
    __arr.push((() => {
      const __arr = [];
      for (let i = 0; i < number; i++) {
        __arr.push(React.createElement("li", { "data-index": i }, `${i + 1}`));
      }
      return __arr;
    })());
    return __arr;
  })()));
  return __arr;
})()));

TabScript ( tjs ) 语法介绍

基于缩进的 JavaScript

初衷

正常开发时,代码缩进是代码风格中不可缺少的一部分,所以有一部分的分隔字符是可以被省略的

下文使用的常见词申明

  • js - JavaScript 语言
  • tjs - TabScript 语言
  • tjsx - TabScript 语言中会被编译成 创建元素的格式语法
  • tjson模式 - TabScript 语言且会被编译成 导出单个对象的代码内容
  • tjsx模式 - TabScript 语言且会被编译成 导出单个函数的代码内容
  • 缩进 - 在代码行首出现的 tab 或者 空格 字符串

简介

  • tjs 本质上是书写风格的 语法糖 ,使用 js 语法,最终还是会编译成 js
  • 不使用 段落花括号,依赖 缩进
  • 不使用 逻辑关键字后的圆括号,依赖 关键字
  • 不使用 行尾操作符,;:,依赖 段落
  • 为书写的基础单位
  • 一次缩进 识别为 段落
  • 两次缩进 识别为 延行,即该行为前行的一部分;缩进和延行可以混合使用
  • 空行注释 是在解析时被认为是无意义的,不影响换行和缩进

特定操作符及关键字

  • 单行的 -{ } 的占位符;比如生成一个作用域块、对象或者占位
  • 行尾的 +[ ] 的占位符;应该只为了生成一个数组的包裹
  • 行尾的 #显式 iife 关键字;生成一个自执行的箭头函数
  • 行首的 #tjsx 的关键字;在与 tjs 混合书写时创建元素解构,类似无 < > 包裹的 jsx
  • 单行的 --空段落 的占位符;只用于了书写时折叠代码,对输出无影响
  • for as 语句中出现的 astostep 关键字;生成用于遍历数值/数组/对象的 for 条件语句
  • 隐式 iife 语句中的 push 关键字;根据参数个数来在自执行函数中生成一个 array or object

引入 iife 模式的意义:

  • 尽可能多的定义为常量
  • 充分利用 return 关键字来避免命名一个临时的变量名

书写建议

尽量使用 tab 做缩进符,默认解析 两个空格 为一个 tab 尽量使用单一缩进方式,默认支持tab空格在单行混用(混用时会触发警告提示) 尽量使用更短更多的行

语法说明

语法示例

变量声明

tjs:

// 单行变量定义方式
// 该处行尾的 ; 因为换行所以可省略
const a = 1

// 多行变量定义方式
const
  // 该处行尾的 , 因为换行所以可省略
  b = 2
  c = 3

// 多行变量解构定义方式
// 该处的 花括号 因为缩进所以可省略
const
  d
  e
= obj

// 定义一个对象
const obj = { d: 4, e: 5 }
const obj2 =
  f: 6
  g: 7

// 定义一个数组
const arr = [ 1, 2 ]
const arr = [
  1
  2
]
// 行尾的 + 号是方括号占位符,所以方括号可省略
const arr = +
  1
  2
  
// 定义一个多层对象
const obj3 = 
  a: 
    b: 0
  c: +
    // 行首的 - 号是花括号占位符,所以花括号可省略
    -
      a: 1
      b: 1
    -
      a: 2
      b: 2
  
// 定义一个函数
const fn = a => a + 1
const fn = a =>
    a + 1
const fn = a =>
  a + 1
const fn = function(a)
  return a + 1
const fn = a =>
  return a + 1

js:

// 单行变量定义方式
// 该处行尾的 ; 因为换行所以可省略
const a = 1;

// 多行变量定义方式
const 

  // 该处行尾的 , 因为换行所以可省略
  b = 2,
  c = 3;

// 多行变量解构定义方式
// 该处的 花括号 因为缩进所以可省略
const {
  d,
  e
} = obj;

// 定义一个对象
const obj = { d: 4, e: 5 };
const obj2 = {
  f: 6,
  g: 7
};

// 定义一个数组
const arr = [ 1, 2 ];
const arr = [
  1,
  2
];

// 行尾的 + 号是方括号占位符,所以方括号可省略
const arr = [
  1,
  2
];

// 定义一个多层对象
const obj3 = {
  a: { b: 0 },
  c: [
    // 行首的 - 号是花括号占位符,所以花括号可省略
    {
      a: 1,
      b: 1
    },
    {
      a: 2,
      b: 2
    }
  ]
};

// 定义一个函数
const fn = a => a + 1;
const fn = a => a + 1;
const fn = a => {
  a + 1;
}
const fn = function (a) {
  return a + 1;
}
const fn = a => {
  return a + 1;
}

if 语句

tjs:

// 单条件判断
// 该处的 圆括号 因为关键字 if 所以可省略
// 该处的 花括号 因为缩进所以可省略
if a > 0
  console.log('a 大于 0')

// 多条件判断
if a > 0 || b > 0
  console.log('a 或 b 大于 0')

// 多条件判断时使用延行
if a > 0 || b > 0
    || c > 0
  console.log('a 或 b 或 c 大于 0')
  
// 多判断时
if a > 0
  console.log('a 大于 0')
else if b > 0
  console.log('b 大于 0')
  
// 多判断时
if a > 0
  console.log('a 大于 0')
// else if 中的 if 可省略
else b > 0
  console.log('b 大于 0')
else
  console.log('a 或 b 都不大于 0')

js:

// 单条件判断
// 该处的 圆括号 因为关键字 if 所以可省略
// 该处的 花括号 因为缩进所以可省略
if (a > 0) {
  console.log('a 大于 0');
}

// 多条件判断
if (a > 0 || b > 0) {
  console.log('a 或 b 大于 0');
}

// 多条件判断时使用延行
if (a > 0 || b > 0 || c > 0) {
  console.log('a 或 b 或 c 大于 0');
}

// 多判断时
if (a > 0) {
  console.log('a 大于 0');
}
else if (b > 0) {
  console.log('b 大于 0');
}

// 多判断时
if (a > 0) {
  console.log('a 大于 0');
}

// else if 中的 if 可省略
else if (b > 0) {
  console.log('b 大于 0');
}
else {
  console.log('a 或 b 都不大于 0');
}

select 语句

selectswitch 语句的 语法糖 在有匹配段落代码的时,在段落尾自动填充 break 关键字 在有变量声明的匹配段落里自动包裹花括号

tjs:

// 常见匹配
select a
  // 该处的 : 因为段落的 select 关键字所以可省略
  case 1
    console.log('a 等于 1')
  case 2
  case 3
    console.log('a 等于 2 或 3')
  default
    console.log('a 不等于 1 或 2 或 3')

// 简写方式
select a
  // 该处的 case 因为段落的 select 关键字所以可省略
  1
    console.log('a 等于 1')
  2
  3
    console.log('a 等于 2 或 3')
  default
    console.log('a 不等于 1 或 2 或 3')

// 混合变量定义
select a
  // 该处的 case 因为段落的 select 关键字所以可省略
  1
    const b = 1
  2
    const b = 2
  default
    console.log('a 不等于 1 或 2')

js:

// 常见匹配
switch (a) {
  // 该处的 : 因为段落的 select 关键字所以可省略
  case 1: 
    console.log('a 等于 1');
    break;
  case 2:
  case 3: 
    console.log('a 等于 2 或 3');
    break;
  default: 
    console.log('a 不等于 1 或 2 或 3');
}

// 简写方式
switch (a) {
  // 该处的 case 因为段落的 select 关键字所以可省略
  case 1: 
    console.log('a 等于 1');
    break;
  case 2:
  case 3: 
    console.log('a 等于 2 或 3');
    break;
  default: 
    console.log('a 不等于 1 或 2 或 3');
}

// 混合变量定义
switch (a) {
  // 该处的 case 因为段落的 select 关键字所以可省略
  case 1: {
    const b = 1;
    break;
  }
  case 2: {
    const b = 2;
    break;
  }
  default: 
    console.log('a 不等于 1 或 2');
}

for as 语句

for asfor 语句的 语法糖 形如: for [<number|expression> to] <object|array|number|expression> [as ...<literal|array|object>] [step <number|expression>]

关键字说明:

to - 该关键字前的一个字面量或表达式作为一个索引初始值;默认值正序时为 0,倒序时为 数值或数组长度减一 as - 该关键字前指定一个用于变量的 对象或数组或数值;关键字后指定为一个或多个用于遍历的形参 step - 该关键字后指定一个字面量或表达式用于步增叠加;默认值为 1

形参数量说明:

形参数量为 0 时:遍历数值 形参数量为 1 时:遍历数值;形参为索引 形参数量为 2 时:遍历数组或类数组;形参依次为值、索引 形参数量为 3 时:遍历对象,使用Object.keys()获取对象键列表,再遍历;形参依次为值、键、键索引

tjs:

// 遍历数值 - 形参数量为 1
// 引入 as 关键字
for 3 as i
  console.log(i)

// 引入 step 关键字;步增值,默认为 1;书写时带 - 时,会自动倒叙变量
for 3 as i step -1
  console.log(i)

// 引入 to 关键字;步增值,默认为 1;书写时带 - 时,会自动倒叙变量
for 1 to 3 as i step -1
  console.log(i)

// 遍历数值时支持不使用形参
for 3
  console.log(1)

// 遍历数组 - 形参数量为 2
for [ 1, 2, 3] as v, i
  console.log(v, i)

// 行尾的 - 为无用变量占位符
for [ 1, 2, 3] as v, -
  console.log(v)

// 形参中所出现的 , 可省略
for [ 1, 2, 3] as v -
  console.log(v)

// 仅遍历数组长度时
for [ 1, 2, 3] as - i
  console.log(i)

// 遍历对象 - 形参数量为 3
for { a: 1, b: 2} as v, k, i
  console.log(v, k)

for { a: 1, b: 2} as v k -
  console.log(v, k)

// 仅遍历对象的键时
for { a: 1, b: 2} as - k -
  console.log(k)

// 仅遍历对象的值时
for { a: 1, b: 2} as v - -
  console.log(v)

js:

// 遍历数值 - 形参数量为 1
// 引入 as 关键字
for (let i = 0; i < 3; i++) {
  console.log(i);
}

// 引入 step 关键字;步增值,默认为 1;书写时带 - 时,会自动倒叙变量
for (let i = 3 - 1; i >= 0; i--) {
  console.log(i);
}

// 引入 to 关键字;步增值,默认为 1;书写时带 - 时,会自动倒叙变量
for (let i = 3 - 1; i >= 1; i--) {
  console.log(i);
}

// 遍历数值时支持不使用形参
for (let __i = 0; __i < 3; __i++) {
  console.log(1);
}

// 遍历数组 - 形参数量为 2
for (let __a = [ 1, 2, 3 ], i = 0; i < __a.length; i++) {
  let v = __a[i];
  console.log(v, i);
}

// 行尾的 - 为无用变量占位符
for (let __a = [ 1, 2, 3 ], __i = 0; __i < __a.length; __i++) {
  let v = __a[__i];
  console.log(v);
}

// 形参中所出现的 , 可省略
for (let __a = [ 1, 2, 3 ], __i = 0; __i < __a.length; __i++) {
  let v = __a[__i];
  console.log(v);
}

// 仅遍历数组长度时
for (let __a = [ 1, 2, 3 ], i = 0; i < __a.length; i++) {
  console.log(i);
}

// 遍历对象 - 形参数量为 3
for (let __o = { a: 1, b: 2 }, __a = Object.keys(__o), i = 0; i < __a.length; i++) {
  let k = __a[i], v = __o[k];
  console.log(v, k);
}
for (let __o = { a: 1, b: 2 }, __a = Object.keys(__o), __i = 0; __i < __a.length; __i++) {
  let k = __a[__i], v = __o[k];
  console.log(v, k);
}

// 仅遍历对象的键时
for (let __o = { a: 1, b: 2 }, __a = Object.keys(__o), __i = 0; __i < __a.length; __i++) {
  let k = __a[__i];
  console.log(k);
}

// 仅遍历对象的值时
for (let __o = { a: 1, b: 2 }, __a = Object.keys(__o), __i = 0; __i < __a.length; __i++) {
  let v = __o[__a[__i]];
  console.log(v);
}

显式及隐式 iife 语句

push 关键字说明:

该关键字用来在隐式iife语句首部生成一个数组或对象,并在尾部返回 形如: push <literal|expression>[, <literal|expression>] 在后面两个参数均为字面量时,逗号可以被省略

参数数量为 1 时:生成一个 空数组,并将 参数 push 至该数组 形参数量为 2 时:生成一个 对象,并将参数分别作为 keyvalue 设置至该对象

tjs:

// 定义一个临时变量方便下文使用
const a = 1

// 显式的 iife 语句
const b = #
  if a > 0
    return a
  else
    return 0

const c = 0 + #
  if a > 0
    return a
  else
    return 0

// 隐式的 if - iife 语句
const d = if a > 0
  return a
else
  return 0

// 隐式的取值 iife 语句
const e = 1 + if a > 0
  return a
else
  return 0

// 隐式的 select - iife 语句
const f = select a
  undefined
  null
    return 0
  default
    if a > 0
      return a
    else
      return 0

// 隐式的 for as - iife 语句
// 模拟 Array.map
const arr = for [ 1, 2 ] as v -
  // 引入 push 关键字
  push v * v

// 模拟 Array.filter
const arr2 = for [ 1, 2 ] as v -
  if v > 0
    push v

// 模拟 Array.filter and Array.map
const arr3 = for [ 1, 2 ] as v -
  if v > 0
    push v * v

// 模拟 Array.find
const value = for [ 1, 2 ] as v -
  if v > 0
    return v

// 模拟 Array.findIndex
const index = for [ 1, 2 ] as v i
  if v > 0
    return i

const arr4 = for 3 as v
  push v * v

// 模拟 object.map;遍历数组时的iife方式均适用于 遍历对象和数值
const obj = for { a: 1, b: 2} as v k -
  push v * v, k

js:

// 定义一个临时变量方便下文使用
const a = 1;

// 显式的 iife 语句
const b = (() => {
  if (a > 0) {
    return a;
  }
  else {
    return 0;
  }
})();
const c = 0 + (() => {
  if (a > 0) {
    return a;
  }
  else {
    return 0;
  }
})();

// 隐式的 if - iife 语句
const d = (() => {
  if (a > 0) {
    return a;
  }
  else {
    return 0;
  }
})();

// 隐式的取值 iife 语句
const e = 1 + (() => {
  if (a > 0) {
    return a;
  }
  else {
    return 0;
  }
})();

// 隐式的 select - iife 语句
const f = (() => {
  switch (a) {
    case undefined:
    case null: 
      return 0;
    default: 
      if (a > 0) {
        return a;
      }
      else {
        return 0;
      }
  }
})();

// 隐式的 for as - iife 语句
// 模拟 Array.map
const arr = (() => {
  const __arr = [];
  for (let __a = [ 1, 2 ], __i = 0; __i < __a.length; __i++) {
    let v = __a[__i];

    // 引入 push 关键字
    __arr.push(v * v);
  }
  return __arr;
})();

// 模拟 Array.filter
const arr2 = (() => {
  const __arr = [];
  for (let __a = [ 1, 2 ], __i = 0; __i < __a.length; __i++) {
    let v = __a[__i];
    if (v > 0) {
      __arr.push(v);
    }
  }
  return __arr;
})();

// 模拟 Array.filter and Array.map
const arr3 = (() => {
  const __arr = [];
  for (let __a = [ 1, 2 ], __i = 0; __i < __a.length; __i++) {
    let v = __a[__i];
    if (v > 0) {
      __arr.push(v * v);
    }
  }
  return __arr;
})();

// 模拟 Array.find
const value = (() => {
  for (let __a = [ 1, 2 ], __i = 0; __i < __a.length; __i++) {
    let v = __a[__i];
    if (v > 0) {
      return v;
    }
  }
})();

// 模拟 Array.findIndex
const index = (() => {
  for (let __a = [ 1, 2 ], i = 0; i < __a.length; i++) {
    let v = __a[i];
    if (v > 0) {
      return i;
    }
  }
})();
const arr4 = (() => {
  const __arr = [];
  for (let v = 0; v < 3; v++) {
    __arr.push(v * v);
  }
  return __arr;
})();

// 模拟 object.map;遍历数组时的iife方式均适用于 遍历对象和数值
const obj = (() => {
  const __obj = {};
  for (let __o = { a: 1, b: 2 }, __a = Object.keys(__o), __i = 0; __i < __a.length; __i++) {
    let k = __a[__i], v = __o[k];
    __obj[v * v] = k;
  }
  return __obj;
})();

tjsx 语句

tjsx 语句是 创建元素 时的 语法糖 形如 #ul class="list-wrap" id="list" 跟写 xml 时有部分不一致,在属性值默认为 js 语法,支持表达式和值;未指定时默认为undefined tjsx 默认使用的创建方法名为 createELement

tjs:

// 运行时改变用于创建时的方法名
// const createELement = React.createELement

// 编译时改变用于创建时的方法名
// parse(content, { tjsx: 'React.createELement' })

// 书写时改变用于创建时的方法名
// //@tjs-set tjsx = React.createELement

// 这里我们用于演示,创建一个临时的方法
function createElement(tagName, attrs, children)
  if !children && Array.isArray(attrs)
    children = attrs
    attrs = null
  return 
    tagName
    attrs
    children

// 简单示例
const a = #div
  #ul class="list-wrap" active-index ...{ a: 1 }
    #li
      `这里是内容1`
    #li
      "这里是内容2"
    #li
      '这里是内容3'

// 遍历
const b = #div
  #ul class="list-wrap" active-index
    for 3 as i
      #li data-index=i
        `这里是内容${i + 1}`

// 混合语法
const c = #div
  const arr = []
  if arr.length === 0
    #p
      "没有内容"
  else
    #ul class="list-wrap" active-index
      for arr as v i
        #li data-index=i
          `这里是内容${v}`

// 混合渲染
function renderList(arr)
  if arr.length === 0
    return #p
      "没有内容"
  else
    return #ul class="list-wrap" active-index
      for 3 as i
        #li data-index=i
          `这里是内容${i + 1}`

const d = #div
  push renderList([])

js:

// 运行时改变用于创建时的方法名
// const createELement = React.createELement
// 编译时改变用于创建时的方法名
// parse(content, { tjsx: 'React.createELement' })
// 书写时改变用于创建时的方法名
// //@tjs-set tjsx = React.createELement
// 这里我们用于演示,创建一个临时的方法
function createElement(tagName, attrs, children) {
  if (!children && Array.isArray(attrs)) {
    children = attrs;
    attrs = null;
  }
  return {
    tagName,
    attrs,
    children
  };
}

// 简单示例
const a = createElement("div", null, [ createElement("ul", { "class": "list-wrap", "active-index": undefined, ...{ a: 1 } }, [
  createElement("li", null, [ `这里是内容1` ]),
  createElement("li", null, [ "这里是内容2" ]),
  createElement("li", null, [ '这里是内容3' ])
]) ]);

// 遍历
const b = createElement("div", null, [ createElement("ul", { "class": "list-wrap", "active-index": undefined }, (() => {
  const __arr = [];
  for (let i = 0; i < 3; i++) {
    __arr.push(createElement("li", { "data-index": i }, [ `这里是内容${i + 1}` ]));
  }
  return __arr;
})()) ]);

// 混合语法
const c = createElement("div", null, (() => {
  const __arr = [];
  const arr = [];
  if (arr.length === 0) {
    __arr.push(createElement("p", null, [ "没有内容" ]));
  }
  else {
    __arr.push(createElement("ul", { "class": "list-wrap", "active-index": undefined }, (() => {
      const __arr = [];
      for (let i = 0; i < arr.length; i++) {
        let v = arr[i];
        __arr.push(createElement("li", { "data-index": i }, [ `这里是内容${v}` ]));
      }
      return __arr;
    })()));
  }
  return __arr;
})());

// 混合渲染
function renderList(arr) {
  if (arr.length === 0) {
    return createElement("p", null, [ "没有内容" ]);
  }
  else {
    return createElement("ul", { "class": "list-wrap", "active-index": undefined }, (() => {
      const __arr = [];
      for (let i = 0; i < 3; i++) {
        __arr.push(createElement("li", { "data-index": i }, [ `这里是内容${i + 1}` ]));
      }
      return __arr;
    })());
  }
}
const d = createElement("div", null, (() => {
  const __arr = [];
  __arr.push(renderList([]));
  return __arr;
})());

字符串块

字符串块使用三个单/双引号包裹的块; 书写方式类似 markdown 里的代码块 两种引号包裹不一样的地方只有是否默认使用单引号字符串将输出内容包裹 允许指定一个 tagName 来使用相应的处理器来处理该字符串 可以用来根据环境变量的不同导出不一样的代码 其中内置了一个 tagNamemeta 的处理器,用来将内容作为js执行,会将该内容执行后的结果返回

输出字符串的字符串块

tjs:

// 输出字符串的字符串块使用 三个单引号包裹
const a = '''
  // 这里是一个多行文本
  123
  456
'''

// 一个简单的示例
// 使用 meta 处理器执行内容并返回
// 字符串块会将内容自动转换为单引号包裹的字符输出到代码里
// 单行内容时自动在行首添加 return,无需手动返回值
const apiHost = """meta
  location.hostname === 'localhost' ? '/api/' : 'http://api.xxx.com/'
'''

js:

// 输出字符串的字符串块使用 三个单引号包裹
const a = '  // 这里是一个多行文本\n  123\n  456';

// 一个简单的示例
// 使用 meta 处理器执行内容并返回
// 字符串块会将内容自动转换为单引号包裹的字符输出到代码里
// 单行内容时自动在行首添加 return,无需手动返回值
const apiHost = '/api/';
输出字面量的字符串块

tjs:

// 输出字面量的字符串块使用 三个双引号包裹
const a = """
  123
  123
  123
"""

// 一个简单的示例
// 使用 meta 处理器执行内容并返回
// 字符串块会将内容直接输出到代码里
const apiHost = """meta
  location.hostname === 'localhost' ? '"/api/"' : '"http://api.xxx.com/"'
"""

// 单行内容时自动在行首添加 return,无需手动返回值
const b = """meta
  [1, 2, 3].map(v => v * 2)
"""
// 多行内容时需手动 return
const c = """meta
  const arr = [1, 2, 3].map(v => v * 2)
  return JSON.stringify(arr)
"""

js:

// 输出字面量的字符串块使用 三个双引号包裹
const a =   123
  123
  123;

// meta 是一个内置的处理器,改内容使用js语法,会将该内容执行后返回的字符串当作字面量输出会代码里
// 单行内容时自动在行首添加 return,无需手动返回值
const b = [2,4,6];

// 多行内容时需手动 return
const c = [2,4,6];

// 一个简单的示例
// 使用 meta 处理器执行内容并返回
// 字符串块会将内容直接输出到代码里
const apiHost = "/api/";

其他

使用 tjs-parser 编译 在webpack中搭配 tjs-loader 进行开发 使用 在线工具 试一试

交流QQ群: 363319058 邮件交流: mrbrick@yinhe.org

Readme

Keywords

Package Sidebar

Install

npm i tjs-parser

Weekly Downloads

0

Version

1.4.0

License

MIT

Unpacked Size

223 kB

Total Files

5

Last publish

Collaborators

  • mrbrick