-
Notifications
You must be signed in to change notification settings - Fork 4
CustomTemplate
本扩展定义了一个配置文件,该配置文件定义了,需要询问用户输入那些参数、每个参数输入框展示什么样的UI、参数的建议值是什么、是否渲染注释、注释配置如何等等。
如果想要自定义模板需要,克隆下来本扩展的 template 目录到本地。
git clone https://github.com/rectcircle/new-file-by-type.git
或者 点此 下载,并解压。
然后指定配置
{
"new-file-by-type.global.templatePath": "clone下来的项目的template目录"
}
然后在template目录下添加模板的配置文件。模板目录的组织结构和配置文件语法,参见下文。
最后执行 本扩展的 重新加载模板 命令
模板目录之下有4个子目录
- new —— 文件模板存放位置
- search —— 网络搜索相关的配置
- translate —— 翻译配置
- i18n —— 全局国际化目录
模板目录的每个子孙目录(i18n目录的除外),都存在一个名字空间,名字空间以是相对于模板根目录的相对路径(使用 .
分割)。例如 ${template}/new/java
的 名字空间为 new.java
,${template}/new/java/common/class
的名字空间为 new.java.common.class
。
模板目录的每个子孙目录有两种类型
-
模板集合
目录:含有子目录的目录(i18n目录除外) -
模板定义
目录:只含有文件的目录
模板集合
是包含其他 模板集合
或 模板定义
的目录。模板定义
是单纯的模板配置文件所在的目录。
模板集合
或 模板定义
目录都允许包含:
- 名为
config.jsonc
的配置文件 - 名为
i18n
的目录 - 其他在
config.jsonc
中引用的文件
重要提示:子目录会继承父目录的配置,同时子目录可以覆写父目录的配置
在 template 目录及子孙目录中名为 config.jsonc
的文件是本扩展的配置文件。配置文件格式是名为 json5 扩展的 JSON 格式,支持//
和/**/
注释。
配置文件支持嵌入JavaScript语法,Json
文件中所有的 value
允许使用 {{ Statement }}
书写 JavaScript 语句,在读取配置文件时,会在适当的时机使用 JavaScript eval
函数执行 JavaScript 语句以获取结果,同时在还提供一些常用的内置函数,还允许用户自定义函数,这提供了巨大的灵活性。
更多详细的配置参见 配置文件和模板文件支持的 模板引擎
本项目提供了配置文件的 json-schema
。在自定义模板时,可以参考或者配置到VSCode中以进行提示
{
"json.schemas": [
{
"fileMatch": ["/template/config.jsonc", "/template/**/config.jsonc"],
"url": "/src/config.schema.json"
},
],
}
{
"name": "默认从key为`文件夹名.name`的i18n文件中读取",
"description": "默认从key为`文件夹名.description`的i18n文件中读取",
"version": "0.0.1",
"suffix": "",
"flat": false,
"renderComment": true,
"indent": "{{defaultConf.indent}}",
"user": "{{defaultConf.user}}",
"author": [
{
"name": "Rectcircle",
"homePage": "https://www.rectcircle.cn"
}
],
"placeHolder": "{{i18n('templateSelectPlaceHolder')}}",
"showHidden": false,
"targets": [{
"filepath": "{{ path.resolve(projectFolder, inputs.srcPath, inputs.filename + ( suffix ? '.' + suffix : ''))}}",
"tplpath": "tpl",
"saveType": "override"
}],
"inputs": [
{
"type": "path",
"name": "srcPath",
"prompt": "{{i18n('inputs.srcPath.prompt')}}",
"placeHolder": "{{i18n('inputs.srcPath.placeHolder')}}",
"value": "{{helper.activeDirectoryRelativeBasePath(projectFolder)}}",
"suggest": {
"value": "src"
}
},
{
"type": "text",
"name": "filename",
"prompt": "{{i18n('inputs.filename.prompt')}}",
"placeHolder": "{{i18n('inputs.filename.placeHolder')}}",
"suggest": {
"selected": true,
"value": "main"
}
}
],
"comment": {
"copyright": "Copyright (c) {{year}}, {{user}}. All rights reserved.",
"dateFormat": "YYYY-MM-DD",
"startLine": "/**",
"lineHeader": " * ",
"endLine": " */",
"items": [
"{{comment.copyright}}",
"",
"@author {{user}}",
"@date {{date}}",
"@version {{version}}"
]
},
"match": {
"always": true
}
}
key | type | 默认值 | 描述 |
---|---|---|---|
name | string | "{{ i18n(`${dir}.name`) }}" | template or type name(模板或模板目录名) |
description | string | "{{ i18n(`${dir}.descriptin`) }}" | template or type description(模板或模板目录描述) |
weight | number | 100 | Template sort weight, The value is small in front(排序权重, 小的在前面) |
version | string | "0.0.1" | default version will use in comment(默认版本,将会渲染在模板配置中) |
flat | boolean | false | subtype flat display(是否展平) |
renderComment | boolean | Whether to render comments(是否渲染注释) | |
indent | number | "{{defaultConf.indent}}" | file indent type: 0 - use Tab, non-zero - use indent number space(缩进类型0-tab, 非0-空格) |
user | string | "{{defaultConf.user}}" | coder name can use in tpl (用户名,将会渲染在注释中) |
author | array | template author(模板作者,支持多个) | |
author[].name | string | author name(模板作者名) | |
author[].email | string | author email(模板作者email) | |
author[].homePage | string | author home page(模板作者个人主页) | |
suffix | string | "" | file suffix(将要创建的文件后缀) |
showHidden | boolean | false | show hidden directory(目录选择器是否显示隐藏文件) |
placeHolder | string | "{{i18n('templateSelectPlaceHolder')}}" | template select view placeHolder text(模板选择器提示文本) |
comment | object | comment config(注释相关配置) | |
comment.copyright | string | "Copyright (c) {{year}}, {{user}}. All rights reserved." | copyright string |
comment.dateFormat | string | "YYYY-MM-DD" | code comment date format(日期格式化) |
comment.startLine | string | "/**" | comment first line string(注释起始内容) |
comment.lineHeader | string | "* " | expect first and last line, comment every line header string(注释每一行起始字符串) |
comment.endLine | string | " */" | comment last line string(注释每一行结束字符串) |
comment.items | array | comment items (注释需要渲染的每一项) | |
match | object | show when this condition is matched(根据条件选择是否显示该模板) | |
match.always | boolean | true | Whether it always matches(是否总是启用该模板) |
match.workspaceFolderGlobs | array | workspace folder glob match(工作空间文件glob匹配表达式) |
该配置项定义了用户输入项(或者是配置项),是核心配置。类型为 array
。
目前支持4中 inputs[].type
输入类型分别是:
- path 文件路径
- text 文本
- select 选择器
- search 搜索框
用户每完成一个输入,模板引擎内的内置变量 inputs
对象将添加一个 key
为 inputs[].name
的 值
全部配置如下
key | type | 默认值 | 描述 |
---|---|---|---|
inputs[].type | enum("path", "text", "select", "search") | 无,必填 | input type(输入类型) |
inputs[].name | string | 无,必填 | input name(变量名) |
inputs[].value | string | 无,选填 | if set value, will not show the input windows(如果配置了该值,将不会展示输入框,该输入将作为配置项) |
inputs[].prompt | string | "{{i18n(`inputs.${name}.prompt`)}}" | prompt(提示) |
inputs[].placeHolder | string | "{{i18n(`inputs.${name}.placeHolder`)}}" | placeHolder(输入框内提示) |
inputs[].items | `array | promise<array> | ((keyword: string) => array |
inputs[].suggest | object | 建议值 | |
inputs[].suggest.selected | boolean | true | 是否选中建议值 |
inputs[].suggest.value | `array | string` | 无,选填 |
inputs[].checkRules | `CheckRule | array` | 无,选填 |
inputs[].option | object | 无,选填 | 其他选项 |
inputs[].option.parentDirectoryText | string | "{{i18n(`inputs.${name}.parentDirectoryText`)}}" | 父目录展示名 |
inputs[].option.confirmText | string | "{{i18n(`inputs.${name}.confirmText`)}}" | 确认项文本 |
inputs[].option.pathSeparator | string | "{{ path.sep }}" | path 分隔符,Linux为/ windows为\\
|
inputs[].option.suggestText | string | "{{i18n(`inputs.${name}.suggestText`)}}" | 建议值标签 |
inputs[].option.directoryText | string | "{{i18n(`inputs.${name}.directoryText`)}}" | 目录项标签 |
inputs[].option.fileText | string | "{{i18n(`inputs.${name}.fileText`)}}" | 文件项标签 |
inputs[].option.confirmDetailText | string | "{{i18n(`inputs.${name}.confirmDetailText`)}}" | 确认项detail文本 |
inputs[].option.currentDirectoryText | string | "{{i18n(`inputs.${name}.currentDirectoryText`)}}" | 当前目录文本 |
inputs[].option.allowNoExist | boolean | true | 是否允许选择不存在的路径 |
inputs[].option.resultExistAndTypeErrorText | string | "{{i18n(`inputs.${name}.resultExistAndTypeErrorText`)}}" | 选择内容类型与要求类型不匹配错误提示文本 |
inputs[].option.returnType | enum("file" , "directory" , "all") | "directory" | 选择目标的类型 |
inputs[].option.basePath | string | {{projectFolder}} | 在哪个目录下选择子目录 |
inputs[].option.canSelectMany | boolean | false | 是否选择多个 |
inputs[].option.canSelectEmpty | boolean | false | 多选是否可以选择0个 |
inputs[].option.multiConfirmText | string | "{{i18n(`inputs.${name}.multiConfirmText`)}}" | 未使用 |
inputs[].option.multiConfirmDetailText | string | "{{i18n(`inputs.${name}.multiConfirmDetailText`)}}" | 多选确认Detail文本 |
inputs[].option.multiSelectCancelText | string | "{{i18n(`inputs.${name}.multiSelectCancelText`)}}" | 多选取消文本 |
类型定义如下
type CheckRule = (value: string) => string | undefined | Promise<string | undefined>;
type Item = string | {
label: string;
description?: string;
detail?: string;
picked?: boolean;
alwaysShow?: boolean;
value: any;
}
path 类型的配置
key | type | 默认值 | 描述 |
---|---|---|---|
inputs[].type | enum("path", "text", "select", "search") | 无,必填 | input type(输入类型) |
inputs[].name | string | 无,必填 | input name(变量名) |
inputs[].value | string | 无,选填 | if set value, will not show the input windows(如果配置了该值,将不会展示输入框,该输入将作为配置项) |
inputs[].prompt | string | "{{i18n(`inputs.${name}.prompt`)}}" | prompt(提示) |
inputs[].placeHolder | string | "{{i18n(`inputs.${name}.placeHolder`)}}" | placeHolder(输入框内提示) |
inputs[].suggest | object | 建议值 | |
inputs[].suggest.value | `array | string` | 无,选填 |
inputs[].checkRules | `CheckRule | array` | 无,选填 |
inputs[].option | object | 无,选填 | 其他选项 |
inputs[].option.parentDirectoryText | string | "{{i18n(`inputs.${name}.parentDirectoryText`)}}" | 父目录展示名 |
inputs[].option.confirmText | string | "{{i18n(`inputs.${name}.confirmText`)}}" | 确认项文本 |
inputs[].option.pathSeparator | string | "{{ path.sep }}" | path 分隔符,Linux为/ windows为\\
|
inputs[].option.suggestText | string | "{{i18n(`inputs.${name}.suggestText`)}}" | 建议值标签 |
inputs[].option.directoryText | string | "{{i18n(`inputs.${name}.directoryText`)}}" | 目录项标签 |
inputs[].option.fileText | string | "{{i18n(`inputs.${name}.fileText`)}}" | 文件项标签 |
inputs[].option.confirmDetailText | string | "{{i18n(`inputs.${name}.confirmDetailText`)}}" | 确认项detail文本 |
inputs[].option.currentDirectoryText | string | "{{i18n(`inputs.${name}.currentDirectoryText`)}}" | 当前目录文本 |
inputs[].option.allowNoExist | boolean | true | 是否允许选择不存在的路径 |
inputs[].option.resultExistAndTypeErrorText | string | "{{i18n(`inputs.${name}.resultExistAndTypeErrorText`)}}" | 选择内容类型与要求类型不匹配错误提示文本 |
inputs[].option.returnType | enum("file" , "directory" , "all") | "directory" | 选择目标的类型 |
inputs[].option.basePath | string | {{projectFolder}} | 在哪个目录下选择子目录 |
inputs[].option.canSelectMany | boolean | false | 是否选择多个 |
inputs[].option.canSelectEmpty | boolean | false | (多选有效)多选是否可以选择0个 |
inputs[].option.multiConfirmText | string | "{{i18n(`inputs.${name}.multiConfirmText`)}}" | 未使用 |
inputs[].option.multiConfirmDetailText | string | "{{i18n(`inputs.${name}.multiConfirmDetailText`)}}" | 多选确认Detail文本 |
inputs[].option.multiSelectCancelText | string | "{{i18n(`inputs.${name}.multiSelectCancelText`)}}" | 多选取消文本 |
key | type | 默认值 | 描述 |
---|---|---|---|
inputs[].type | enum("path", "text", "select", "search") | 无,必填 | input type(输入类型) |
inputs[].name | string | 无,必填 | input name(变量名) |
inputs[].value | string | 无,选填 | if set value, will not show the input windows(如果配置了该值,将不会展示输入框,该输入将作为配置项) |
inputs[].prompt | string | "{{i18n(`inputs.${name}.prompt`)}}" | prompt(提示) |
inputs[].placeHolder | string | "{{i18n(`inputs.${name}.placeHolder`)}}" | placeHolder(输入框内提示) |
inputs[].suggest | object | 建议值 | |
inputs[].suggest.selected | boolean | true | 是否选中建议值,如果多个,选第一个 |
inputs[].suggest.value | `array | string` | 无,选填 |
inputs[].checkRules | `CheckRule | array` | 无,选填 |
key | type | 默认值 | 描述 |
---|---|---|---|
inputs[].type | enum("path", "text", "select", "search") | 无,必填 | input type(输入类型) |
inputs[].name | string | 无,必填 | input name(变量名) |
inputs[].value | string | 无,选填 | if set value, will not show the input windows(如果配置了该值,将不会展示输入框,该输入将作为配置项) |
inputs[].placeHolder | string | "{{i18n(`inputs.${name}.placeHolder`)}}" | placeHolder(输入框内提示) |
inputs[].items | `array | promise<array> ` | 无,选填 |
inputs[].checkRules | `CheckRule | array` | 无,选填 |
inputs[].option | object | 无,选填 | 其他选项 |
inputs[].option.canSelectMany | boolean | false | 是否选择多个 |
key | type | 默认值 | 描述 |
---|---|---|---|
inputs[].type | enum("path", "text", "select", "search") | 无,必填 | input type(输入类型) |
inputs[].name | string | 无,必填 | input name(变量名) |
inputs[].value | string | 无,选填 | if set value, will not show the input windows(如果配置了该值,将不会展示输入框,该输入将作为配置项) |
inputs[].prompt | string | "{{i18n(`inputs.${name}.prompt`)}}" | prompt(提示) |
inputs[].placeHolder | string | "{{i18n(`inputs.${name}.placeHolder`)}}" | placeHolder(输入框内提示) |
inputs[].items | `(keyword: string) => array | promise<array>` | 无,选填 |
inputs[].suggest | object | 建议值 | |
inputs[].suggest.selected | boolean | true | 是否选中建议值(由于vscode不支持所以暂时无效) |
inputs[].suggest.value | `array | string>` | 无,选填 |
inputs[].option | object | 无,选填 | 其他选项 |
inputs[].option.canSelectMany | boolean | false | 是否选择多个 |
本扩展不仅支持将最终渲染的模板写入文件,还支持写入剪切板,打开浏览器等功能。本扩展支持多个输出目标
key | type | 默认值 | 描述 |
---|---|---|---|
targets[].targetType | emun("file", "clipboard", "command", "browser") | "file" | 目标输出类型 |
targets[].filepath | string | "{{ path.resolve(projectFolder, inputs.srcPath, inputs.filename + ( suffix ? '.' + suffix : ''))}}" | target file path to generate(文件写入的目标路径) |
targets[].tplpath | string | "tpl" | template file path(模板文件的路径) |
targets[].tplcontent | string | "tpl" | template content, Priority is higher than tplpath(模板内容,比tplpath有更高的优先级) |
targets[].saveType | enum("override", "append", "insert") | "override" | tpl save type (targetType = "file")(文件方式时,保存方式) |
注意:
-
tplcontent
比tplpath
有更高的优先级 -
tplcontent
和tplpath
指向的文件同样支持模板语法{{}}
- 当
targetType == 'file'
时,filepath
和saveType
配置有效 - 当
targetType == 'clipboard'
时,模板渲染结果将写入剪切板 - 当
targetType == 'command'
时,模板渲染结果作为命令在shell中执行 - 当
targetType == 'browser'
时,模板渲染结果作为url打开浏览器
{
"declaration" : "declaration.js"
}
自定义声明文件路径,只支持相对路径,相对于当前配置文件所在目录。声明文件支持JavaScript语法(NodeJs 10.x)。
不同于普通的NodeJs模块,声明文件不支持export等模块化操作,如果想在配置文件和模板中使用。请将变量或函数赋值为 declaration
。
为了方便调试,本扩展提供了模板引擎上下文文件 context.js
,使用特殊的注释标记 /*<...>*/ /*<.../>*/
可以在解析该文件时,自动忽略测试内容。
例子 template/new/js/declaration.js
// 使用 /*<...>*/这里是包裹的代码 /*<.../>*/ 包裹的代码,eval执行之前会被删除,用于测试时使用
/*<...>*/
const {
path,
fs,
os,
axios,
moment,
cheerio,
happyCoding,
happyCodingString,
language,
openedFilePaths,
defaultConf,
encoding,
useActive,
activeDirectory,
now,
year,
name,
description,
suffix,
flat,
indent,
user,
placeHolder,
targets,
version,
comment,
declaration,
customize,
Date,
projectFolder,
commentOutput,
inputs,
helper,
i18n,
checkRules,
} = require('../../../context');
/*<.../>*/
// 以下是自定义函数的正文, 请将的声明的函数或者变量放置到 declaration 中
/**
* 根据 targetPath(当前文件) 和 importPaths (导入文件绝对路径),解析文件名和相对路径
* @param {string} targetPath 当前文件
* @param {string[]} 要导入的文件
* @param {(name: string, relative: string, extname: string) => string} 提取出数据如何使用的函数
*/
declaration['nodeImports'] = function (targetPath, importPaths, toString) {
let result = [];
let targetDirPath = path.dirname(targetPath);
for (let p of importPaths) {
let importFullPath = path.resolve(projectFolder, p);
let filename = path.basename(p);
let extname = path.extname(filename);
filename = filename.replace(extname, '');
let relative = path.relative(targetDirPath, importFullPath);
if (!relative.startsWith('.')) {
relative = './' + relative;
}
relative = relative.replace(extname, '');
result.push(toString(filename, relative, extname));
}
if (result.length === 0) {
return '';
}
return result.join('\n') + '\n';
};
/*<...>*/
async function test() {
const result = await declaration['nodeImports'](
'/a/b/project/src/test/test.js',
['/a/b/project/src/main.js', '/a/b/project/src/lib/util.js'],
(name, relative) => `import * as ${name} from '${relative}'`);
console.log(result)
}
test();
/*<.../>*/
template 任意子孙目录下均允许存在i18n目录,该目录下包含以小写国家码为名字的json文件(注意不是json5格式,是标准json格式)。
json文件定义的键值对,通过 i18n
函数可以根据VSCode选择的语言选取不同文件中的值作为返回值。如果不存在将返回警告字符串,而不会抛出异常
为了最大的灵活性,本扩展实现了一个简易的模板引擎。
该模板引擎JavaScript基于 函数闭包
和 eval
实现全功能的JavaScript的模板支持。
模板引擎提供了很多内置的模块、变量和函数,均可通过 {{}}
访问。
详细定义可以参见 context.js
以下两个地方的使用 {{}}
包裹
config.jsonc
-
config.jsonc
配置的targets[].tplpath
指向的模板文件
注意在 config.jsonc
中使用
-
"key": "这是字符串的其他内容 {{ source }}"
将eval(source)
并转换为字符串 并替换{{ source }}
-
"key": "{{ source }}
将eval(source)
并不会转换为字符串,保留原有类型
此外针对 config.jsonc
的配置 declaration
指向的声明文件
- 将可以直接使用模板引擎内置的变量、函数和模块
- 不支持模块化语法
- 最佳实践是将 要在配置或模板使用的函数或变量赋值给内置的
declaration
变量
- path
- fs
- os
- axios http客户端
- moment 日期时间库
- cheerio nodejs dom操纵
- happyCoding,
- happyCodingString,
- language,
- openedFilePaths,
- defaultConf, 将根据用户系统状态获取
- encoding,
- useActive,
- activeDirectory,
- now,
- year,
- name,
- description,
- suffix,
- flat,
- indent,
- user,
- placeHolder,
- targets,
- version,
- comment,
- declaration,
- customize,
- date,
- projectFolder,
- commentOutput,
- inputs,
- helper,
- i18n,
- checkRules,