refactor(@vant/cli): remove vetur configs (#10866)
This commit is contained in:
@@ -1,25 +0,0 @@
|
||||
import markdownVetur from '@vant/markdown-vetur';
|
||||
import {
|
||||
SRC_DIR,
|
||||
VETUR_DIR,
|
||||
getVantConfig,
|
||||
getPackageJson,
|
||||
} from '../common/constant.js';
|
||||
|
||||
// generate vetur tags & attributes
|
||||
export function genVeturConfig() {
|
||||
const pkgJson = getPackageJson();
|
||||
const vantConfig = getVantConfig();
|
||||
const options = vantConfig.build?.vetur;
|
||||
|
||||
if (options) {
|
||||
markdownVetur.parseAndWrite({
|
||||
name: vantConfig.name,
|
||||
path: SRC_DIR,
|
||||
test: /README\.md/,
|
||||
version: pkgJson.version,
|
||||
outputDir: VETUR_DIR,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/* eslint-disable no-continue */
|
||||
import { Articles } from './parser.js';
|
||||
import {
|
||||
formatOptions,
|
||||
formatType,
|
||||
removeVersion,
|
||||
toKebabCase,
|
||||
} from './utils.js';
|
||||
import { VueEventArgument, VueTag } from './type.js';
|
||||
|
||||
function formatComponentName(name: string, tagPrefix: string) {
|
||||
return tagPrefix + toKebabCase(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* format arguments of events
|
||||
* input = value: { foo: foo or 1, bar: bar or 2 }, value2: { one: 1 and 1, two: 2 and 2 }, foo: bar
|
||||
* output = [{ name: 'value', type: '{ foo: foo or 1, bar: bar or 2 }' }, { name: 'value2', type: '{ one: 1 and 1, two: 2 and 2 }'}, { name: 'foo', type: 'bar' }]
|
||||
*/
|
||||
function formatArguments(input: string): VueEventArgument[] {
|
||||
if (input === '-') return [];
|
||||
const args: VueEventArgument[] = [];
|
||||
const items = [];
|
||||
input = formatType(input);
|
||||
while (input.length > 0) {
|
||||
if (/(?!_)\w/.test(input[0])) {
|
||||
const val = input.match(/(\w|\s|\p{P}|\||\[|\]|>|<)+/)![0] || '';
|
||||
input = input.substring(val.length);
|
||||
items.push(val);
|
||||
} else if (input[0] === '{') {
|
||||
const val = input.match(/\{[^}]+\}/)![0] || '';
|
||||
input = input.substring(val.length);
|
||||
items.push(val);
|
||||
} else if ([':', ',', '_', ' '].includes(input[0])) {
|
||||
input = input.substring(1);
|
||||
} else {
|
||||
const val = input.match(/( |'|\||\w)+/)![0] || '';
|
||||
input = input.substring(val.length);
|
||||
items.push(val);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < items.length; i += 2) {
|
||||
args.push({
|
||||
name: items[i],
|
||||
type: items[i + 1],
|
||||
});
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
function getNameFromTableTitle(tableTitle: string, tagPrefix: string) {
|
||||
tableTitle = tableTitle.trim();
|
||||
if (tableTitle.includes(' ')) {
|
||||
return formatComponentName(tableTitle, tagPrefix).split(' ')[0];
|
||||
}
|
||||
}
|
||||
|
||||
function findTag(vueTags: VueTag[], name: string) {
|
||||
const matched = vueTags.find((item) => item.name === name);
|
||||
|
||||
if (matched) {
|
||||
return matched;
|
||||
}
|
||||
|
||||
const newTag: VueTag = {
|
||||
name,
|
||||
slots: [],
|
||||
events: [],
|
||||
attributes: [],
|
||||
};
|
||||
|
||||
vueTags.push(newTag);
|
||||
|
||||
return newTag;
|
||||
}
|
||||
|
||||
export function formatter(
|
||||
vueTags: VueTag[],
|
||||
articles: Articles,
|
||||
tagPrefix = ''
|
||||
) {
|
||||
if (!articles.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mainTitle = articles[0].content;
|
||||
const defaultName = mainTitle
|
||||
? formatComponentName(mainTitle.split(' ')[0], tagPrefix)
|
||||
: '';
|
||||
const tables = articles.filter((article) => article.type === 'table');
|
||||
|
||||
tables.forEach((item) => {
|
||||
const { table } = item;
|
||||
const prevIndex = articles.indexOf(item) - 1;
|
||||
const prevArticle = articles[prevIndex];
|
||||
|
||||
if (!prevArticle || !prevArticle.content || !table || !table.body) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tableTitle = prevArticle.content;
|
||||
|
||||
if (tableTitle.includes('Props')) {
|
||||
const name = getNameFromTableTitle(tableTitle, tagPrefix) || defaultName;
|
||||
const tag = findTag(vueTags, name);
|
||||
|
||||
table.body.forEach((line) => {
|
||||
const [name, desc, type, defaultVal, options] = line;
|
||||
tag.attributes!.push({
|
||||
name: removeVersion(name),
|
||||
default: defaultVal,
|
||||
description: desc,
|
||||
options: formatOptions(options),
|
||||
value: {
|
||||
type: formatType(type),
|
||||
kind: 'expression',
|
||||
},
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (tableTitle.includes('Events')) {
|
||||
const name = getNameFromTableTitle(tableTitle, tagPrefix) || defaultName;
|
||||
const tag = findTag(vueTags, name);
|
||||
|
||||
table.body.forEach((line) => {
|
||||
const [name, desc, args] = line;
|
||||
tag.events!.push({
|
||||
name: removeVersion(name),
|
||||
description: desc,
|
||||
arguments: formatArguments(args),
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (tableTitle.includes('Slots')) {
|
||||
const name = getNameFromTableTitle(tableTitle, tagPrefix) || defaultName;
|
||||
const tag = findTag(vueTags, name);
|
||||
|
||||
table.body.forEach((line) => {
|
||||
const [name, desc] = line;
|
||||
tag.slots!.push({
|
||||
name: removeVersion(name),
|
||||
description: desc,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import glob from 'fast-glob';
|
||||
import { join } from 'path';
|
||||
import fse from 'fs-extra';
|
||||
import { mdParser } from './parser.js';
|
||||
import { formatter } from './formatter.js';
|
||||
import { genWebTypes } from './web-types.js';
|
||||
import { Options, VueTag } from './type.js';
|
||||
import { normalizePath } from './utils.js';
|
||||
import {
|
||||
SRC_DIR,
|
||||
LIB_DIR,
|
||||
getVantConfig,
|
||||
getPackageJson,
|
||||
} from '../../common/constant.js';
|
||||
|
||||
async function readMarkdown(options: Options) {
|
||||
const mds = await glob(normalizePath(`${options.path}/**/*.md`));
|
||||
return mds
|
||||
.filter((md) => options.test.test(md))
|
||||
.map((path) => fse.readFileSync(path, 'utf-8'));
|
||||
}
|
||||
|
||||
export async function parseAndWrite(options: Options) {
|
||||
if (!options.outputDir) {
|
||||
throw new Error('outputDir can not be empty.');
|
||||
}
|
||||
|
||||
const mds = await readMarkdown(options);
|
||||
const vueTags: VueTag[] = [];
|
||||
|
||||
mds.forEach((md) => {
|
||||
const parsedMd = mdParser(md);
|
||||
formatter(vueTags, parsedMd, options.tagPrefix);
|
||||
});
|
||||
|
||||
const webTypes = genWebTypes(vueTags, options);
|
||||
fse.outputFileSync(
|
||||
join(options.outputDir, 'web-types.json'),
|
||||
JSON.stringify(webTypes, null, 2)
|
||||
);
|
||||
}
|
||||
|
||||
export function genWebStormTypes() {
|
||||
const pkgJson = getPackageJson();
|
||||
const vantConfig = getVantConfig();
|
||||
|
||||
parseAndWrite({
|
||||
name: vantConfig.name,
|
||||
path: SRC_DIR,
|
||||
test: /README\.md/,
|
||||
version: pkgJson.version,
|
||||
outputDir: LIB_DIR,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/* eslint-disable no-cond-assign */
|
||||
const TITLE_REG = /^(#+)\s+([^\n]*)/;
|
||||
const TABLE_REG = /^\|.+\r?\n\|\s*-+/;
|
||||
const TD_REG = /\s*`[^`]+`\s*|([^|`]+)/g;
|
||||
const TABLE_SPLIT_LINE_REG = /^\|\s*-/;
|
||||
|
||||
type TableContent = {
|
||||
head: string[];
|
||||
body: string[][];
|
||||
};
|
||||
|
||||
export type Article = {
|
||||
type: string;
|
||||
content?: string;
|
||||
table?: TableContent;
|
||||
level?: number;
|
||||
};
|
||||
|
||||
export type Articles = Article[];
|
||||
|
||||
function readLine(input: string) {
|
||||
const end = input.indexOf('\n');
|
||||
|
||||
return input.substr(0, end !== -1 ? end : input.length);
|
||||
}
|
||||
|
||||
function splitTableLine(line: string) {
|
||||
line = line.replace(/\\\|/g, 'JOIN');
|
||||
|
||||
const items = line
|
||||
.split('|')
|
||||
.map((item) => item.trim().replace(/JOIN/g, '|'));
|
||||
|
||||
// remove pipe character on both sides
|
||||
items.pop();
|
||||
items.shift();
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
function tableParse(input: string) {
|
||||
let start = 0;
|
||||
let isHead = true;
|
||||
|
||||
const end = input.length;
|
||||
const table: TableContent = {
|
||||
head: [],
|
||||
body: [],
|
||||
};
|
||||
|
||||
while (start < end) {
|
||||
const target = input.substr(start);
|
||||
const line = readLine(target);
|
||||
|
||||
if (!/^\|/.test(target)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (TABLE_SPLIT_LINE_REG.test(target)) {
|
||||
isHead = false;
|
||||
} else if (!isHead && line.includes('|')) {
|
||||
const matched = line.trim().match(TD_REG);
|
||||
|
||||
if (matched) {
|
||||
table.body.push(splitTableLine(line));
|
||||
}
|
||||
}
|
||||
|
||||
start += line.length + 1;
|
||||
}
|
||||
|
||||
return {
|
||||
table,
|
||||
usedLength: start,
|
||||
};
|
||||
}
|
||||
|
||||
export function mdParser(input: string): Articles {
|
||||
const article = [];
|
||||
let start = 0;
|
||||
const end = input.length;
|
||||
|
||||
while (start < end) {
|
||||
const target = input.substr(start);
|
||||
|
||||
let match;
|
||||
if ((match = TITLE_REG.exec(target))) {
|
||||
article.push({
|
||||
type: 'title',
|
||||
content: match[2],
|
||||
level: match[1].length,
|
||||
});
|
||||
|
||||
start += match.index + match[0].length;
|
||||
} else if ((match = TABLE_REG.exec(target))) {
|
||||
const { table, usedLength } = tableParse(target.substr(match.index));
|
||||
article.push({
|
||||
type: 'table',
|
||||
table,
|
||||
});
|
||||
|
||||
start += match.index + usedLength;
|
||||
} else {
|
||||
start += readLine(target).length + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return article;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { PathLike } from 'fs';
|
||||
|
||||
export type VueSlot = {
|
||||
name: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type VueEventArgument = {
|
||||
name: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export type VueEvent = {
|
||||
name: string;
|
||||
description?: string;
|
||||
arguments?: VueEventArgument[];
|
||||
};
|
||||
|
||||
export type VueAttribute = {
|
||||
name: string;
|
||||
default: string;
|
||||
description: string;
|
||||
options: string[];
|
||||
value: {
|
||||
kind: 'expression';
|
||||
type: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type VueTag = {
|
||||
name: string;
|
||||
slots?: VueSlot[];
|
||||
events?: VueEvent[];
|
||||
attributes?: VueAttribute[];
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export type Options = {
|
||||
name: string;
|
||||
path: PathLike;
|
||||
test: RegExp;
|
||||
version: string;
|
||||
outputDir?: string;
|
||||
tagPrefix?: string;
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
// myName -> my-name
|
||||
export function toKebabCase(input: string): string {
|
||||
return input.replace(
|
||||
/[A-Z]/g,
|
||||
(val, index) => (index === 0 ? '' : '-') + val.toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
// name `v2.0.0` -> name
|
||||
export function removeVersion(str: string) {
|
||||
return str.replace(/`(\w|\.)+`/g, '').trim();
|
||||
}
|
||||
|
||||
// *boolean* -> boolean
|
||||
// _boolean_ -> boolean
|
||||
export function formatType(type: string) {
|
||||
return type.replace(/(^(\*|_))|((\*|_)$)/g, '');
|
||||
}
|
||||
|
||||
export function normalizePath(path: string): string {
|
||||
return path.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
// `default` `primary` -> ['default', 'primary']
|
||||
export function formatOptions(options?: string) {
|
||||
if (!options) return [];
|
||||
return options.replace(/`/g, '').split(' ');
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { VueTag, Options } from './type.js';
|
||||
|
||||
// create web-types.json to provide autocomplete in JetBrains IDEs
|
||||
export function genWebTypes(tags: VueTag[], options: Options) {
|
||||
return {
|
||||
$schema:
|
||||
'https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json',
|
||||
framework: 'vue',
|
||||
name: options.name,
|
||||
version: options.version,
|
||||
contributions: {
|
||||
html: {
|
||||
tags,
|
||||
attributes: [],
|
||||
'types-syntax': 'typescript',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user