mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
Merge pull request #5152 from youzan/feature/vant_cli_2
refactor: vant-cli 2.0
This commit is contained in:
commit
adbaf9b69e
2
.gitignore
vendored
2
.gitignore
vendored
@ -12,7 +12,7 @@ package-lock.json
|
|||||||
es
|
es
|
||||||
lib
|
lib
|
||||||
dist
|
dist
|
||||||
docs/dist
|
./site
|
||||||
changelog.generated.md
|
changelog.generated.md
|
||||||
test/coverage
|
test/coverage
|
||||||
vetur
|
vetur
|
||||||
|
@ -1,38 +1,3 @@
|
|||||||
module.exports = function (api) {
|
module.exports = {
|
||||||
const { BABEL_MODULE, NODE_ENV } = process.env;
|
presets: ['@vant/cli/preset']
|
||||||
const useESModules = BABEL_MODULE !== 'commonjs' && NODE_ENV !== 'test';
|
|
||||||
|
|
||||||
api && api.cache(false);
|
|
||||||
|
|
||||||
return {
|
|
||||||
presets: [
|
|
||||||
[
|
|
||||||
'@babel/preset-env',
|
|
||||||
{
|
|
||||||
loose: true,
|
|
||||||
modules: useESModules ? false : 'commonjs'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'@vue/babel-preset-jsx',
|
|
||||||
{
|
|
||||||
functional: false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'@babel/preset-typescript'
|
|
||||||
],
|
|
||||||
plugins: [
|
|
||||||
[
|
|
||||||
'@babel/plugin-transform-runtime',
|
|
||||||
{
|
|
||||||
corejs: false,
|
|
||||||
helpers: true,
|
|
||||||
regenerator: NODE_ENV === 'test',
|
|
||||||
useESModules
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'@babel/plugin-transform-object-assign',
|
|
||||||
'@babel/plugin-proposal-optional-chaining'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
/**
|
|
||||||
* Compile components
|
|
||||||
*/
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const path = require('path');
|
|
||||||
const babel = require('@babel/core');
|
|
||||||
const markdownVetur = require('@vant/markdown-vetur');
|
|
||||||
|
|
||||||
const esDir = path.join(__dirname, '../es');
|
|
||||||
const libDir = path.join(__dirname, '../lib');
|
|
||||||
const srcDir = path.join(__dirname, '../src');
|
|
||||||
const veturDir = path.join(__dirname, '../vetur');
|
|
||||||
const babelConfig = {
|
|
||||||
configFile: path.join(__dirname, '../babel.config.js')
|
|
||||||
};
|
|
||||||
|
|
||||||
const scriptRegExp = /\.(js|ts|tsx)$/;
|
|
||||||
const isDir = dir => fs.lstatSync(dir).isDirectory();
|
|
||||||
const isCode = path => !/(demo|test|\.md)$/.test(path);
|
|
||||||
const isScript = path => scriptRegExp.test(path);
|
|
||||||
|
|
||||||
function compile(dir) {
|
|
||||||
const files = fs.readdirSync(dir);
|
|
||||||
|
|
||||||
files.forEach(file => {
|
|
||||||
const filePath = path.join(dir, file);
|
|
||||||
|
|
||||||
// remove unnecessary files
|
|
||||||
if (!isCode(file)) {
|
|
||||||
return fs.removeSync(filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// scan dir
|
|
||||||
if (isDir(filePath)) {
|
|
||||||
return compile(filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// compile js or ts
|
|
||||||
if (isScript(file)) {
|
|
||||||
const { code } = babel.transformFileSync(filePath, babelConfig);
|
|
||||||
fs.removeSync(filePath);
|
|
||||||
fs.outputFileSync(filePath.replace(scriptRegExp, '.js'), code);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear dir
|
|
||||||
fs.emptyDirSync(esDir);
|
|
||||||
fs.emptyDirSync(libDir);
|
|
||||||
|
|
||||||
// compile es dir
|
|
||||||
fs.copySync(srcDir, esDir);
|
|
||||||
compile(esDir);
|
|
||||||
|
|
||||||
// compile lib dir
|
|
||||||
process.env.BABEL_MODULE = 'commonjs';
|
|
||||||
fs.copySync(srcDir, libDir);
|
|
||||||
compile(libDir);
|
|
||||||
|
|
||||||
// generate vetur tags & attributes
|
|
||||||
markdownVetur.parseAndWrite({
|
|
||||||
path: srcDir,
|
|
||||||
test: /zh-CN\.md/,
|
|
||||||
tagPrefix: 'van-',
|
|
||||||
outputDir: veturDir
|
|
||||||
});
|
|
@ -1,61 +0,0 @@
|
|||||||
const fs = require('fs-extra');
|
|
||||||
const path = require('path');
|
|
||||||
const uppercamelize = require('uppercamelcase');
|
|
||||||
const Components = require('./get-components')();
|
|
||||||
const packageJson = require('../package.json');
|
|
||||||
|
|
||||||
const version = process.env.VERSION || packageJson.version;
|
|
||||||
const tips = '// This file is auto generated by build/build-entry.js';
|
|
||||||
|
|
||||||
function buildEntry() {
|
|
||||||
const uninstallComponents = [
|
|
||||||
'Locale',
|
|
||||||
'Lazyload',
|
|
||||||
'Waterfall'
|
|
||||||
];
|
|
||||||
|
|
||||||
const importList = Components.map(name => `import ${uppercamelize(name)} from './${name}';`);
|
|
||||||
const exportList = Components.map(name => `${uppercamelize(name)}`);
|
|
||||||
const installList = exportList.filter(name => !~uninstallComponents.indexOf(uppercamelize(name)));
|
|
||||||
const content = `${tips}
|
|
||||||
import { VueConstructor } from 'vue/types';
|
|
||||||
${importList.join('\n')}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
Vue?: VueConstructor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const version = '${version}';
|
|
||||||
const components = [
|
|
||||||
${installList.join(',\n ')}
|
|
||||||
];
|
|
||||||
|
|
||||||
const install = (Vue: VueConstructor) => {
|
|
||||||
components.forEach(Component => {
|
|
||||||
Vue.use(Component);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/* istanbul ignore if */
|
|
||||||
if (typeof window !== 'undefined' && window.Vue) {
|
|
||||||
install(window.Vue);
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
install,
|
|
||||||
version,
|
|
||||||
${exportList.join(',\n ')}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
install,
|
|
||||||
version
|
|
||||||
};
|
|
||||||
`;
|
|
||||||
|
|
||||||
fs.writeFileSync(path.join(__dirname, '../src/index.ts'), content);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildEntry();
|
|
@ -1,34 +0,0 @@
|
|||||||
/**
|
|
||||||
* Build npm lib
|
|
||||||
*/
|
|
||||||
const shell = require('shelljs');
|
|
||||||
const signale = require('signale');
|
|
||||||
|
|
||||||
const { Signale } = signale;
|
|
||||||
const tasks = [
|
|
||||||
'npm run bootstrap',
|
|
||||||
'npm run lint',
|
|
||||||
'npm run build:entry',
|
|
||||||
'node build/build-components.js',
|
|
||||||
'node build/build-style.js',
|
|
||||||
'node build/build-style-entry.js',
|
|
||||||
'cross-env NODE_ENV=production webpack --color --config build/webpack.pkg.js',
|
|
||||||
'cross-env NODE_ENV=production webpack -p --color --config build/webpack.pkg.js'
|
|
||||||
];
|
|
||||||
|
|
||||||
tasks.every(task => {
|
|
||||||
signale.start(task);
|
|
||||||
|
|
||||||
const interactive = new Signale({ interactive: true });
|
|
||||||
interactive.pending(task);
|
|
||||||
|
|
||||||
const result = shell.exec(`${task} --silent`);
|
|
||||||
|
|
||||||
if (result.code !== 0) {
|
|
||||||
interactive.error(task);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
interactive.success(task);
|
|
||||||
return true;
|
|
||||||
});
|
|
@ -1,115 +0,0 @@
|
|||||||
/* eslint-disable no-use-before-define */
|
|
||||||
/**
|
|
||||||
* Build style entry of all components
|
|
||||||
*/
|
|
||||||
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const path = require('path');
|
|
||||||
const dependencyTree = require('dependency-tree');
|
|
||||||
const components = require('./get-components')();
|
|
||||||
|
|
||||||
// replace seq for windows
|
|
||||||
function replaceSeq(path) {
|
|
||||||
return path.split(path.sep).join('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
const whiteList = [
|
|
||||||
'info',
|
|
||||||
'icon',
|
|
||||||
'loading',
|
|
||||||
'cell',
|
|
||||||
'cell-group',
|
|
||||||
'button',
|
|
||||||
'overlay'
|
|
||||||
];
|
|
||||||
const dir = path.join(__dirname, '../es');
|
|
||||||
|
|
||||||
function destEntryFile(component, filename, ext = '') {
|
|
||||||
const deps = analyzeDependencies(component).map(dep =>
|
|
||||||
getStyleRelativePath(component, dep, ext)
|
|
||||||
);
|
|
||||||
|
|
||||||
const esEntry = path.join(dir, component, `style/${filename}`);
|
|
||||||
const libEntry = path.join(
|
|
||||||
__dirname,
|
|
||||||
'../lib',
|
|
||||||
component,
|
|
||||||
`style/${filename}`
|
|
||||||
);
|
|
||||||
const esContent = deps.map(dep => `import '${dep}';`).join('\n');
|
|
||||||
const libContent = deps.map(dep => `require('${dep}');`).join('\n');
|
|
||||||
|
|
||||||
fs.outputFileSync(esEntry, esContent);
|
|
||||||
fs.outputFileSync(libEntry, libContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// analyze component dependencies
|
|
||||||
function analyzeDependencies(component) {
|
|
||||||
const checkList = ['base'];
|
|
||||||
|
|
||||||
search(
|
|
||||||
dependencyTree({
|
|
||||||
directory: dir,
|
|
||||||
filename: path.join(dir, component, 'index.js'),
|
|
||||||
filter: path => !~path.indexOf('node_modules')
|
|
||||||
}),
|
|
||||||
component,
|
|
||||||
checkList
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!whiteList.includes(component)) {
|
|
||||||
checkList.push(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
return checkList.filter(item => checkComponentHasStyle(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
function search(tree, component, checkList) {
|
|
||||||
Object.keys(tree).forEach(key => {
|
|
||||||
search(tree[key], component, checkList);
|
|
||||||
components
|
|
||||||
.filter(item =>
|
|
||||||
key
|
|
||||||
.replace(dir, '')
|
|
||||||
.split('/')
|
|
||||||
.includes(item)
|
|
||||||
)
|
|
||||||
.forEach(item => {
|
|
||||||
if (
|
|
||||||
!checkList.includes(item) &&
|
|
||||||
!whiteList.includes(item) &&
|
|
||||||
item !== component
|
|
||||||
) {
|
|
||||||
checkList.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStylePath(component, ext = '.css') {
|
|
||||||
if (component === 'base') {
|
|
||||||
return path.join(__dirname, `../es/style/base${ext}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.join(__dirname, `../es/${component}/index${ext}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStyleRelativePath(component, style, ext) {
|
|
||||||
return replaceSeq(
|
|
||||||
path.relative(
|
|
||||||
path.join(__dirname, `../es/${component}/style`),
|
|
||||||
getStylePath(style, ext)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkComponentHasStyle(component) {
|
|
||||||
return fs.existsSync(getStylePath(component));
|
|
||||||
}
|
|
||||||
|
|
||||||
components.forEach(component => {
|
|
||||||
// css entry
|
|
||||||
destEntryFile(component, 'index.js', '.css');
|
|
||||||
// less entry
|
|
||||||
destEntryFile(component, 'less.js', '.less');
|
|
||||||
});
|
|
@ -1,66 +0,0 @@
|
|||||||
const fs = require('fs-extra');
|
|
||||||
const glob = require('fast-glob');
|
|
||||||
const path = require('path');
|
|
||||||
const less = require('less');
|
|
||||||
const csso = require('csso');
|
|
||||||
const postcss = require('postcss');
|
|
||||||
const postcssrc = require('postcss-load-config');
|
|
||||||
|
|
||||||
async function compileLess(lessCodes, paths) {
|
|
||||||
const outputs = await Promise.all(
|
|
||||||
lessCodes.map((source, index) =>
|
|
||||||
less.render(source, {
|
|
||||||
paths: [path.resolve(__dirname, 'node_modules')],
|
|
||||||
filename: paths[index]
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return outputs.map(item => item.css);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function compilePostcss(cssCodes, paths) {
|
|
||||||
const postcssConfig = await postcssrc();
|
|
||||||
const outputs = await Promise.all(
|
|
||||||
cssCodes.map((css, index) =>
|
|
||||||
postcss(postcssConfig.plugins).process(css, { from: paths[index] })
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return outputs.map(item => item.css);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function compileCsso(cssCodes) {
|
|
||||||
return cssCodes.map(css => csso.minify(css).css);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function dest(output, paths) {
|
|
||||||
await Promise.all(
|
|
||||||
output.map((css, index) => fs.writeFile(paths[index].replace('.less', '.css'), css))
|
|
||||||
);
|
|
||||||
|
|
||||||
// icon.less should be replaced by compiled file
|
|
||||||
const iconCss = await glob(['./es/icon/*.css', './lib/icon/*.css'], { absolute: true });
|
|
||||||
iconCss.forEach(file => {
|
|
||||||
fs.copyFileSync(file, file.replace('.css', '.less'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// compile component css
|
|
||||||
async function compile() {
|
|
||||||
let codes;
|
|
||||||
try {
|
|
||||||
const paths = await glob(['./es/**/*.less', './lib/**/*.less'], { absolute: true });
|
|
||||||
|
|
||||||
codes = await Promise.all(paths.map(path => fs.readFile(path, 'utf-8')));
|
|
||||||
codes = await compileLess(codes, paths);
|
|
||||||
codes = await compilePostcss(codes, paths);
|
|
||||||
codes = await compileCsso(codes);
|
|
||||||
|
|
||||||
await dest(codes, paths);
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compile();
|
|
@ -1,10 +0,0 @@
|
|||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
const EXCLUDES = ['index.ts', 'index.less', 'style', 'mixins', 'utils', '.DS_Store'];
|
|
||||||
|
|
||||||
module.exports = function() {
|
|
||||||
const src = path.resolve(__dirname, '../src');
|
|
||||||
const dirs = fs.readdirSync(src);
|
|
||||||
return dirs.filter(dir => !EXCLUDES.includes(dir));
|
|
||||||
};
|
|
@ -1,28 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
set -e
|
|
||||||
echo "Enter release version: "
|
|
||||||
read VERSION
|
|
||||||
|
|
||||||
read -p "Releasing $VERSION - are you sure? (y/n)" -n 1 -r
|
|
||||||
echo # (optional) move to a new line
|
|
||||||
if [[ $REPLY =~ ^[Yy]$ ]]
|
|
||||||
then
|
|
||||||
# build
|
|
||||||
npm version $VERSION --no-git-tag-version
|
|
||||||
VERSION=$VERSION npm run build:lib
|
|
||||||
|
|
||||||
# commit
|
|
||||||
git tag v$VERSION
|
|
||||||
git commit -am "build: release $VERSION"
|
|
||||||
|
|
||||||
# publish
|
|
||||||
git push origin dev
|
|
||||||
git push origin refs/tags/v$VERSION
|
|
||||||
|
|
||||||
if [[ $VERSION =~ [beta] ]]
|
|
||||||
then
|
|
||||||
npm publish --tag beta
|
|
||||||
else
|
|
||||||
npm publish
|
|
||||||
fi
|
|
||||||
fi
|
|
@ -1,57 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const { VueLoaderPlugin } = require('vue-loader');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
mode: 'development',
|
|
||||||
resolve: {
|
|
||||||
extensions: ['.js', '.ts', '.tsx', '.vue', '.less']
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.vue$/,
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
loader: 'vue-loader',
|
|
||||||
options: {
|
|
||||||
compilerOptions: {
|
|
||||||
preserveWhitespace: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.(js|ts|tsx)$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
use: {
|
|
||||||
loader: 'babel-loader',
|
|
||||||
// enable sub-packages to find babel config
|
|
||||||
options: {
|
|
||||||
rootMode: 'upward'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.less$/,
|
|
||||||
sideEffects: true,
|
|
||||||
use: [
|
|
||||||
'style-loader',
|
|
||||||
'css-loader',
|
|
||||||
'postcss-loader',
|
|
||||||
{
|
|
||||||
loader: 'less-loader',
|
|
||||||
options: {
|
|
||||||
paths: [path.resolve(__dirname, 'node_modules')]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.md$/,
|
|
||||||
use: ['vue-loader', '@vant/markdown-loader']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
plugins: [new VueLoaderPlugin()]
|
|
||||||
};
|
|
@ -1,33 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const merge = require('webpack-merge');
|
|
||||||
const config = require('./webpack.base');
|
|
||||||
|
|
||||||
const isMinify = process.argv.indexOf('-p') !== -1;
|
|
||||||
|
|
||||||
module.exports = merge(config, {
|
|
||||||
mode: 'production',
|
|
||||||
entry: {
|
|
||||||
vant: './es/index.js'
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
path: path.join(__dirname, '../lib'),
|
|
||||||
library: 'vant',
|
|
||||||
libraryTarget: 'umd',
|
|
||||||
filename: isMinify ? '[name].min.js' : '[name].js',
|
|
||||||
umdNamedDefine: true,
|
|
||||||
// https://github.com/webpack/webpack/issues/6522
|
|
||||||
globalObject: 'typeof self !== \'undefined\' ? self : this'
|
|
||||||
},
|
|
||||||
externals: {
|
|
||||||
vue: {
|
|
||||||
root: 'Vue',
|
|
||||||
commonjs: 'vue',
|
|
||||||
commonjs2: 'vue',
|
|
||||||
amd: 'vue'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
performance: false,
|
|
||||||
optimization: {
|
|
||||||
minimize: isMinify
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,47 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const merge = require('webpack-merge');
|
|
||||||
const config = require('./webpack.base');
|
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|
||||||
|
|
||||||
module.exports = merge(config, {
|
|
||||||
entry: {
|
|
||||||
'vant-docs': './docs/site/desktop/main.js',
|
|
||||||
'vant-mobile': './docs/site/mobile/main.js'
|
|
||||||
},
|
|
||||||
devServer: {
|
|
||||||
open: true,
|
|
||||||
progress: true,
|
|
||||||
host: '0.0.0.0',
|
|
||||||
stats: 'errors-only',
|
|
||||||
disableHostCheck: true,
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
path: path.join(__dirname, '../docs/dist'),
|
|
||||||
publicPath: '/',
|
|
||||||
chunkFilename: 'async_[name].js'
|
|
||||||
},
|
|
||||||
optimization: {
|
|
||||||
splitChunks: {
|
|
||||||
cacheGroups: {
|
|
||||||
chunks: {
|
|
||||||
chunks: 'all',
|
|
||||||
minChunks: 2,
|
|
||||||
minSize: 0,
|
|
||||||
name: 'chunks'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
chunks: ['chunks', 'vant-docs'],
|
|
||||||
template: path.join(__dirname, '../docs/site/desktop/index.html'),
|
|
||||||
filename: 'index.html'
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
chunks: ['chunks', 'vant-mobile'],
|
|
||||||
template: path.join(__dirname, '../docs/site/mobile/index.html'),
|
|
||||||
filename: 'mobile.html'
|
|
||||||
})
|
|
||||||
]
|
|
||||||
});
|
|
@ -1,13 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const merge = require('webpack-merge');
|
|
||||||
const config = require('./webpack.site.dev');
|
|
||||||
|
|
||||||
module.exports = merge(config, {
|
|
||||||
mode: 'production',
|
|
||||||
output: {
|
|
||||||
path: path.join(__dirname, '../docs/dist'),
|
|
||||||
publicPath: 'https://b.yzcdn.cn/vant/',
|
|
||||||
filename: '[name].[hash:8].js',
|
|
||||||
chunkFilename: 'async_[name].[chunkhash:8].js'
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,149 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="side-nav">
|
|
||||||
<div class="mobile-switch-lang">
|
|
||||||
<span
|
|
||||||
:class="{ active: $vantLang === 'zh-CN' }"
|
|
||||||
@click="switchLang('zh-CN')"
|
|
||||||
>
|
|
||||||
中文
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
:class="{ active: $vantLang === 'en-US' }"
|
|
||||||
@click="switchLang('en-US')"
|
|
||||||
>
|
|
||||||
EN
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1 class="vant-title">
|
|
||||||
<img src="https://img.yzcdn.cn/vant/logo.png">
|
|
||||||
<span>Vant</span>
|
|
||||||
</h1>
|
|
||||||
<h2 class="vant-desc">{{ description }}</h2>
|
|
||||||
<template v-for="item in navList">
|
|
||||||
<mobile-nav
|
|
||||||
v-for="(group, index) in item.groups"
|
|
||||||
:group="group"
|
|
||||||
:base="$vantLang"
|
|
||||||
:key="index"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import docConfig from '../doc.config';
|
|
||||||
import MobileNav from './MobileNav';
|
|
||||||
import { setLang } from '../utils/lang';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
MobileNav
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
docConfig
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
navList() {
|
|
||||||
return (this.docConfig[this.$vantLang].nav || []).filter(item => item.showInMobile);
|
|
||||||
},
|
|
||||||
|
|
||||||
description() {
|
|
||||||
return this.$vantLang === 'zh-CN' ? '轻量、可靠的移动端 Vue 组件库' : 'Mobile UI Components built on Vue';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
switchLang(lang) {
|
|
||||||
const from = lang === 'zh-CN' ? 'en-US' : 'zh-CN';
|
|
||||||
this.$router.push(this.$route.path.replace(from, lang));
|
|
||||||
setLang(lang);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
@import '../../../src/style/var';
|
|
||||||
|
|
||||||
.side-nav {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 100vh;
|
|
||||||
padding: 46px 20px 20px;
|
|
||||||
background: @white;
|
|
||||||
|
|
||||||
.vant-title,
|
|
||||||
.vant-desc {
|
|
||||||
padding-left: @padding-md;
|
|
||||||
font-weight: normal;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vant-title {
|
|
||||||
margin: 0 0 @padding-md;
|
|
||||||
|
|
||||||
img,
|
|
||||||
span {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
margin-left: @padding-md;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 36px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.vant-desc {
|
|
||||||
margin: 0 0 40px;
|
|
||||||
color: #7d7e80;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-switch-lang {
|
|
||||||
position: absolute;
|
|
||||||
top: 20px;
|
|
||||||
right: 20px;
|
|
||||||
overflow: hidden;
|
|
||||||
color: @blue;
|
|
||||||
font-size: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
span {
|
|
||||||
display: inline-block;
|
|
||||||
width: 48px;
|
|
||||||
color: @text-color;
|
|
||||||
line-height: 22px;
|
|
||||||
text-align: center;
|
|
||||||
background-color: #fff;
|
|
||||||
border: 1px solid @border-color;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
border-right: none;
|
|
||||||
border-radius: 3px 0 0 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-left: none;
|
|
||||||
border-radius: 0 3px 3px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: @white;
|
|
||||||
background-color: @blue;
|
|
||||||
border-color: @blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,74 +0,0 @@
|
|||||||
<template>
|
|
||||||
<van-collapse
|
|
||||||
v-model="active"
|
|
||||||
:border="false"
|
|
||||||
class="mobile-nav"
|
|
||||||
>
|
|
||||||
<van-collapse-item
|
|
||||||
class="mobile-nav__item"
|
|
||||||
:title="group.groupName"
|
|
||||||
:name="group.groupName"
|
|
||||||
>
|
|
||||||
<van-icon
|
|
||||||
:name="group.icon"
|
|
||||||
slot="right-icon"
|
|
||||||
class="mobile-nav__icon"
|
|
||||||
/>
|
|
||||||
<template v-for="(navItem, index) in group.list">
|
|
||||||
<van-cell
|
|
||||||
v-if="!navItem.disabled"
|
|
||||||
:key="index"
|
|
||||||
:to="'/' + base + navItem.path"
|
|
||||||
:title="navItem.title"
|
|
||||||
is-link
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</van-collapse-item>
|
|
||||||
</van-collapse>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
base: String,
|
|
||||||
group: Object
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
active: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.mobile-nav {
|
|
||||||
&__item {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
border-radius: 6px;
|
|
||||||
box-shadow: 0 1px 5px #ebedf0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__icon {
|
|
||||||
font-size: 24px;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-collapse-item__content {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-collapse-item__title {
|
|
||||||
align-items: center;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 40px;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,74 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="app">
|
|
||||||
<van-doc
|
|
||||||
:base="base"
|
|
||||||
:config="config"
|
|
||||||
:lang="$vantLang"
|
|
||||||
:github="github"
|
|
||||||
:versions="versions"
|
|
||||||
:simulators="simulators"
|
|
||||||
:search-config="searchConfig"
|
|
||||||
:current-simulator="currentSimulator"
|
|
||||||
@switch-version="onSwitchVersion"
|
|
||||||
>
|
|
||||||
<router-view @changeDemoURL="onChangeDemoURL" />
|
|
||||||
</van-doc>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import pkgJson from '../../../package.json';
|
|
||||||
import docConfig, { github, versions, searchConfig } from '../doc.config';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
this.github = github;
|
|
||||||
this.versions = versions;
|
|
||||||
this.searchConfig = searchConfig;
|
|
||||||
|
|
||||||
return {
|
|
||||||
simulators: [`mobile.html${location.hash}`],
|
|
||||||
demoURL: ''
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
base() {
|
|
||||||
return `/${this.$vantLang}`;
|
|
||||||
},
|
|
||||||
|
|
||||||
config() {
|
|
||||||
return docConfig[this.$vantLang];
|
|
||||||
},
|
|
||||||
|
|
||||||
currentSimulator() {
|
|
||||||
const { name } = this.$route;
|
|
||||||
return name && name.indexOf('demo') !== -1 ? 1 : 0;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
onChangeDemoURL(url) {
|
|
||||||
this.simulators = [this.simulators[0], url];
|
|
||||||
},
|
|
||||||
|
|
||||||
onSwitchVersion(version) {
|
|
||||||
if (version !== pkgJson.version) {
|
|
||||||
location.href = `https://youzan.github.io/vant/${version}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.van-doc-intro {
|
|
||||||
padding-top: 20px;
|
|
||||||
font-family: 'Dosis', 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,25 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
|
|
||||||
<link rel="shortcut icon" href="https://img.yzcdn.cn/zanui/vant/vant-2017-12-18.ico">
|
|
||||||
<link href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" rel="stylesheet" />
|
|
||||||
<title>Vant - 轻量、可靠的移动端 Vue 组件库</title>
|
|
||||||
<script>
|
|
||||||
var _hmt = _hmt || [];
|
|
||||||
(function() {
|
|
||||||
var hm = document.createElement("script");
|
|
||||||
hm.src = "https://hm.baidu.com/hm.js?ad6b5732c36321f2dafed737ac2da92f";
|
|
||||||
var s = document.getElementsByTagName("script")[0];
|
|
||||||
s.parentNode.insertBefore(hm, s);
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body ontouchstart>
|
|
||||||
|
|
||||||
<div id="app"></div>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,60 +0,0 @@
|
|||||||
import Vue from 'vue';
|
|
||||||
import VueRouter from 'vue-router';
|
|
||||||
import VantDoc from '@vant/doc';
|
|
||||||
import App from './App';
|
|
||||||
import routes from '../router';
|
|
||||||
import { isMobile, importAll } from '../utils';
|
|
||||||
|
|
||||||
if (isMobile) {
|
|
||||||
location.replace('mobile.html' + location.hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vue.use(VueRouter).use(VantDoc);
|
|
||||||
|
|
||||||
const docs = {};
|
|
||||||
const docsFromMarkdown = require.context('../../markdown', false, /(en-US|zh-CN)\.md$/);
|
|
||||||
const docsFromPackages = require.context('../../../src', true, /README(\.zh-CN)?\.md$/);
|
|
||||||
|
|
||||||
importAll(docs, docsFromMarkdown);
|
|
||||||
importAll(docs, docsFromPackages);
|
|
||||||
|
|
||||||
const router = new VueRouter({
|
|
||||||
mode: 'hash',
|
|
||||||
routes: routes({ componentMap: docs }),
|
|
||||||
scrollBehavior(to) {
|
|
||||||
if (to.hash) {
|
|
||||||
return { selector: to.hash };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { x: 0, y: 0 };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.afterEach(() => {
|
|
||||||
Vue.nextTick(() => window.syncPath());
|
|
||||||
});
|
|
||||||
|
|
||||||
window.vueRouter = router;
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
|
||||||
Vue.config.productionTip = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
el: '#app',
|
|
||||||
mounted() {
|
|
||||||
if (this.$route.hash) {
|
|
||||||
// wait page init
|
|
||||||
setTimeout(() => {
|
|
||||||
const el = document.querySelector(this.$route.hash);
|
|
||||||
if (el) {
|
|
||||||
el.scrollIntoView({
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
render: h => h(App),
|
|
||||||
router
|
|
||||||
});
|
|
@ -1,24 +1,46 @@
|
|||||||
/**
|
|
||||||
* Demo Common Mixin && i18n
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import VueRouter from 'vue-router';
|
import Locale from '../../src/locale';
|
||||||
import VantDoc from '@vant/doc';
|
import Lazyload from '../../src/lazyload';
|
||||||
import i18n from '../utils/i18n';
|
import { get } from '../../src/utils';
|
||||||
import Vant, { Lazyload, Locale } from '../../../src';
|
import { camelize } from '../../src/utils/format/string';
|
||||||
import { camelize } from '../../../src/utils/format/string';
|
|
||||||
|
|
||||||
Vue
|
Vue.use(Lazyload, {
|
||||||
.use(Vant)
|
lazyComponent: true
|
||||||
.use(VantDoc)
|
});
|
||||||
.use(VueRouter)
|
|
||||||
.use(Lazyload, {
|
|
||||||
lazyComponent: true
|
|
||||||
});
|
|
||||||
|
|
||||||
Vue.mixin(i18n);
|
// helper for demo locales
|
||||||
|
Vue.mixin({
|
||||||
|
computed: {
|
||||||
|
$t() {
|
||||||
|
const { name } = this.$options;
|
||||||
|
const { lang = 'zh-CN' } = (this.$route && this.$route.meta) || {};
|
||||||
|
const prefix = name ? camelize(name) + '.' : '';
|
||||||
|
const messages = this.$vantMessages[lang];
|
||||||
|
|
||||||
|
return (path, ...args) => {
|
||||||
|
const message = get(messages, prefix + path) || get(messages, path);
|
||||||
|
return typeof message === 'function' ? message(...args) : message;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeCreate() {
|
||||||
|
const { i18n, name } = this.$options;
|
||||||
|
|
||||||
|
if (i18n && name) {
|
||||||
|
const locales = {};
|
||||||
|
const camelizedName = camelize(name);
|
||||||
|
|
||||||
|
Object.keys(i18n).forEach(key => {
|
||||||
|
locales[key] = { [camelizedName]: i18n[key] };
|
||||||
|
});
|
||||||
|
|
||||||
|
Locale.add(locales);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// add some basic locale messages
|
||||||
Locale.add({
|
Locale.add({
|
||||||
'zh-CN': {
|
'zh-CN': {
|
||||||
add: '增加',
|
add: '增加',
|
||||||
@ -77,21 +99,3 @@ Locale.add({
|
|||||||
passwordPlaceholder: 'Password'
|
passwordPlaceholder: 'Password'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export function demoWrapper(module, name) {
|
|
||||||
const component = module.default;
|
|
||||||
name = 'demo-' + name;
|
|
||||||
component.name = name;
|
|
||||||
|
|
||||||
const { i18n: config } = component;
|
|
||||||
if (config) {
|
|
||||||
const formattedI18n = {};
|
|
||||||
const camelizedName = camelize(name);
|
|
||||||
Object.keys(config).forEach(key => {
|
|
||||||
formattedI18n[key] = { [camelizedName]: config[key] };
|
|
||||||
});
|
|
||||||
Locale.add(formattedI18n);
|
|
||||||
}
|
|
||||||
|
|
||||||
return component;
|
|
||||||
}
|
|
@ -1,102 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<van-nav-bar
|
|
||||||
v-show="title"
|
|
||||||
class="van-doc-nav-bar"
|
|
||||||
:title="title"
|
|
||||||
:border="false"
|
|
||||||
:left-arrow="showNav"
|
|
||||||
@click-left="onBack"
|
|
||||||
/>
|
|
||||||
<van-notice-bar
|
|
||||||
v-if="weapp"
|
|
||||||
v-show="title"
|
|
||||||
wrapable
|
|
||||||
:text="tips"
|
|
||||||
background="#ecf9ff"
|
|
||||||
color="rgba(52, 73, 94, 0.8)"
|
|
||||||
style="font-size: 12px;"
|
|
||||||
/>
|
|
||||||
<keep-alive>
|
|
||||||
<router-view :weapp="weapp" />
|
|
||||||
</keep-alive>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function getQueryString(name) {
|
|
||||||
const arr = location.search.substr(1).split('&');
|
|
||||||
for (let i = 0, l = arr.length; i < l; i++) {
|
|
||||||
const item = arr[i].split('=');
|
|
||||||
if (item.shift() === name) {
|
|
||||||
return decodeURIComponent(item.join('='));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
computed: {
|
|
||||||
title() {
|
|
||||||
return this.$route.meta.title || '';
|
|
||||||
},
|
|
||||||
|
|
||||||
showNav() {
|
|
||||||
return getQueryString('hide_nav') !== '1';
|
|
||||||
},
|
|
||||||
|
|
||||||
weapp() {
|
|
||||||
return getQueryString('weapp') === '1';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeCreate() {
|
|
||||||
this.tips = 'Tips: 当前预览的是 Vue 版 Vant 的示例,少部分功能可能与小程序版有出入,请以文档描述和实际效果为准。';
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
onBack() {
|
|
||||||
history.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
@import '../../../src/style/var';
|
|
||||||
|
|
||||||
body {
|
|
||||||
min-width: 100vw;
|
|
||||||
color: @text-color;
|
|
||||||
font-family: 'PingFang SC', Helvetica, 'STHeiti STXihei', 'Microsoft YaHei', Tohoma, Arial, sans-serif;
|
|
||||||
line-height: 1;
|
|
||||||
background-color: #f7f8fa;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 0;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-doc-nav-bar {
|
|
||||||
height: 56px;
|
|
||||||
line-height: 56px;
|
|
||||||
|
|
||||||
.van-nav-bar__title {
|
|
||||||
font-size: 17px;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-icon {
|
|
||||||
color: @gray-6;
|
|
||||||
font-size: 24px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-doc-demo-section {
|
|
||||||
margin-top: -56px;
|
|
||||||
padding-top: 56px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,30 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
|
|
||||||
<meta http-Equiv="Cache-Control" Content="no-cache" />
|
|
||||||
<meta http-Equiv="Pragma" Content="no-cache" />
|
|
||||||
<meta http-Equiv="Expires" Content="0" />
|
|
||||||
<link rel="shortcut icon" href="https://img.yzcdn.cn/zanui/vant/vant-2017-12-18.ico">
|
|
||||||
<title>Vant - 轻量、可靠的移动端 Vue 组件库</title>
|
|
||||||
<script>window.Promise || document.write('<script src="//img.yzcdn.cn/huiyi/build/h5/js/pinkie.min.js"><\/script>');</script>
|
|
||||||
<script>
|
|
||||||
// avoid to load analytics in iframe
|
|
||||||
if (window.top === window) {
|
|
||||||
var _hmt = _hmt || [];
|
|
||||||
(function() {
|
|
||||||
var hm = document.createElement("script");
|
|
||||||
hm.src = "https://hm.baidu.com/hm.js?ad6b5732c36321f2dafed737ac2da92f";
|
|
||||||
var s = document.getElementsByTagName("script")[0];
|
|
||||||
s.parentNode.insertBefore(hm, s);
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body ontouchstart>
|
|
||||||
|
|
||||||
<div id="app"></div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,38 +0,0 @@
|
|||||||
import '../../../src/index.less';
|
|
||||||
import Vue from 'vue';
|
|
||||||
import VueRouter from 'vue-router';
|
|
||||||
import routes from '../router';
|
|
||||||
import App from './App';
|
|
||||||
import { importAll } from '../utils';
|
|
||||||
import '@vant/touch-emulator';
|
|
||||||
|
|
||||||
const componentMap = {};
|
|
||||||
const context = require.context('../../../src', true, /demo\/index.vue$/);
|
|
||||||
|
|
||||||
importAll(componentMap, context);
|
|
||||||
|
|
||||||
const router = new VueRouter({
|
|
||||||
mode: 'hash',
|
|
||||||
routes: routes({ mobile: true, componentMap }),
|
|
||||||
scrollBehavior(to, from, savedPosition) {
|
|
||||||
return savedPosition || { x: 0, y: 0 };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.afterEach(() => {
|
|
||||||
if (!router.currentRoute.redirectedFrom) {
|
|
||||||
Vue.nextTick(() => window.syncPath());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
window.vueRouter = router;
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
|
||||||
Vue.config.productionTip = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
el: '#app',
|
|
||||||
render: h => h(App),
|
|
||||||
router
|
|
||||||
});
|
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
rm -rf docs/dist
|
rm -rf docs/dist
|
||||||
|
|
||||||
npx cross-env NODE_ENV=production webpack --config build/webpack.site.prd.js
|
vant-cli build-site
|
||||||
|
|
||||||
superman-cdn /vant ./docs/dist/*.js
|
superman-cdn /vant ./docs/dist/*.js
|
||||||
|
|
@ -1,84 +0,0 @@
|
|||||||
import Vue from 'vue';
|
|
||||||
import docConfig from './doc.config';
|
|
||||||
import DemoList from './components/DemoList';
|
|
||||||
import { demoWrapper } from './mobile/demo-common';
|
|
||||||
import { initIframeRouter } from './utils/iframe-router';
|
|
||||||
|
|
||||||
initIframeRouter();
|
|
||||||
|
|
||||||
const registerRoute = ({ mobile, componentMap }) => {
|
|
||||||
const route = [
|
|
||||||
{
|
|
||||||
path: '*',
|
|
||||||
redirect: () => `/${Vue.prototype.$vantLang}/`
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
Object.keys(docConfig).forEach(lang => {
|
|
||||||
if (mobile) {
|
|
||||||
route.push({
|
|
||||||
path: `/${lang}`,
|
|
||||||
component: DemoList,
|
|
||||||
meta: { lang }
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
route.push({
|
|
||||||
path: `/${lang}`,
|
|
||||||
redirect: `/${lang}/intro`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addRoute(page, lang) {
|
|
||||||
let { path } = page;
|
|
||||||
if (path) {
|
|
||||||
path = path.replace('/', '');
|
|
||||||
|
|
||||||
let component;
|
|
||||||
if (mobile) {
|
|
||||||
const module = componentMap[`./${path}/demo/index.vue`];
|
|
||||||
|
|
||||||
if (module) {
|
|
||||||
component = demoWrapper(module, path);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const module =
|
|
||||||
componentMap[`./${path}/README.${lang}.md`] ||
|
|
||||||
componentMap[`./${path}/README.md`] ||
|
|
||||||
componentMap[`./${path}.${lang}.md`];
|
|
||||||
|
|
||||||
component = module.default;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!component) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
route.push({
|
|
||||||
component,
|
|
||||||
name: `${lang}/${path}`,
|
|
||||||
path: `/${lang}/${path}`,
|
|
||||||
meta: {
|
|
||||||
lang,
|
|
||||||
name: path,
|
|
||||||
title: page.title
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const navs = docConfig[lang].nav || [];
|
|
||||||
navs.forEach(nav => {
|
|
||||||
if (nav.groups) {
|
|
||||||
nav.groups.forEach(group => {
|
|
||||||
group.list.forEach(page => addRoute(page, lang));
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
addRoute(nav, lang);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return route;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default registerRoute;
|
|
@ -1,18 +0,0 @@
|
|||||||
// component mixin
|
|
||||||
import { get } from '../../../src/utils';
|
|
||||||
import { camelize } from '../../../src/utils/format/string';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
computed: {
|
|
||||||
$t() {
|
|
||||||
const { name } = this.$options;
|
|
||||||
const prefix = name ? camelize(name) + '.' : '';
|
|
||||||
const messages = this.$vantMessages[this.$vantLang];
|
|
||||||
|
|
||||||
return (path, ...args) => {
|
|
||||||
const message = get(messages, prefix + path) || get(messages, path);
|
|
||||||
return typeof message === 'function' ? message(...args) : message;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,40 +0,0 @@
|
|||||||
/**
|
|
||||||
* 同步父窗口和 iframe 的 vue-router 状态
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { setLang } from './lang';
|
|
||||||
import { iframeReady, isMobile } from '.';
|
|
||||||
|
|
||||||
export function initIframeRouter() {
|
|
||||||
window.syncPath = function () {
|
|
||||||
const router = window.vueRouter;
|
|
||||||
const isInIframe = window !== window.top;
|
|
||||||
const currentDir = router.history.current.path;
|
|
||||||
const pathParts = currentDir.split('/');
|
|
||||||
let lang = pathParts[0];
|
|
||||||
if (currentDir[0] === '/') {
|
|
||||||
lang = pathParts[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isInIframe && !isMobile) {
|
|
||||||
const iframe = document.querySelector('iframe');
|
|
||||||
if (iframe) {
|
|
||||||
iframeReady(iframe, () => {
|
|
||||||
iframe.contentWindow.changePath(lang, currentDir);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setLang(lang);
|
|
||||||
} else if (isInIframe) {
|
|
||||||
window.top.changePath(lang, currentDir);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.changePath = function (lang, path = '') {
|
|
||||||
setLang(lang);
|
|
||||||
|
|
||||||
// should preserve hash for anchor
|
|
||||||
if (window.vueRouter.currentRoute.path !== path) {
|
|
||||||
window.vueRouter.replace(path).catch(() => {});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
import Locale from '../../../src/locale';
|
|
||||||
import zhCN from '../../../src/locale/lang/zh-CN';
|
|
||||||
import enUS from '../../../src/locale/lang/en-US';
|
|
||||||
|
|
||||||
const langMap = {
|
|
||||||
'en-US': {
|
|
||||||
title: 'Vant - Mobile UI Components built on Vue',
|
|
||||||
messages: enUS
|
|
||||||
},
|
|
||||||
'zh-CN': {
|
|
||||||
title: 'Vant - 轻量、可靠的移动端 Vue 组件库',
|
|
||||||
messages: zhCN
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let currentLang = '';
|
|
||||||
|
|
||||||
function getDefaultLang() {
|
|
||||||
const langs = Object.keys(langMap);
|
|
||||||
const { hash } = location;
|
|
||||||
|
|
||||||
for (let i = 0; i < langs.length; i++) {
|
|
||||||
if (hash.indexOf(langs[i]) !== -1) {
|
|
||||||
return langs[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const userLang = localStorage.getItem('VANT_LANGUAGE') || navigator.language || 'en-US';
|
|
||||||
return userLang.indexOf('zh-') !== -1 ? 'zh-CN' : 'en-US';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setLang(lang) {
|
|
||||||
if (currentLang === lang) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentLang = lang;
|
|
||||||
if (window.localStorage) {
|
|
||||||
localStorage.setItem('VANT_LANGUAGE', lang);
|
|
||||||
}
|
|
||||||
Locale.use(lang, langMap[lang].messages);
|
|
||||||
document.title = langMap[lang].title;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLang(getDefaultLang());
|
|
@ -1,18 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
moduleFileExtensions: ['js', 'jsx', 'vue', 'ts', 'tsx'],
|
|
||||||
transform: {
|
|
||||||
'\\.(vue)$': 'vue-jest',
|
|
||||||
'\\.(js|jsx|ts|tsx)$': '<rootDir>/test/transformer.js',
|
|
||||||
},
|
|
||||||
snapshotSerializers: ['jest-serializer-vue'],
|
|
||||||
collectCoverageFrom: [
|
|
||||||
'src/**/*.{js,jsx,ts,tsx,vue}',
|
|
||||||
'!**/style/**',
|
|
||||||
'!**/demo/**',
|
|
||||||
'!**/locale/lang/**',
|
|
||||||
'!**/sku/**'
|
|
||||||
],
|
|
||||||
collectCoverage: true,
|
|
||||||
coverageReporters: ['html', 'lcov', 'text-summary'],
|
|
||||||
coverageDirectory: './test/coverage'
|
|
||||||
};
|
|
74
package.json
74
package.json
@ -14,31 +14,29 @@
|
|||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bootstrap": "yarn || npm i",
|
"bootstrap": "yarn || npm i",
|
||||||
"dev": "npm run build:entry && webpack-dev-server --config build/webpack.site.dev.js",
|
"dev": "vant-cli dev",
|
||||||
"lint": "eslint ./src --ext .js,.vue,.ts,.tsx && stylelint \"src/**/*.less\" --fix",
|
"lint": "vant-cli lint",
|
||||||
"build:entry": "node build/build-entry.js",
|
"test": "vant-cli test",
|
||||||
"build:changelog": "vant changelog ./docs/markdown/changelog.generated.md --tag v2.1.0",
|
"build": "vant-cli build",
|
||||||
"build:lib": "node build/build-lib.js",
|
"release": "vant-cli release",
|
||||||
"test": "jest",
|
"test:watch": "vant-cli test --watch",
|
||||||
"test:watch": "jest --watch",
|
"release:site": "sh docs/site/release.sh",
|
||||||
"test:clear-cache": "jest --clearCache",
|
|
||||||
"test:coverage": "open test/coverage/index.html",
|
"test:coverage": "open test/coverage/index.html",
|
||||||
"release": "sh build/release.sh",
|
"changelog": "vant-cli changelog ./docs/changelog.generated.md"
|
||||||
"release:site": "sh build/release-site.sh"
|
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
"pre-commit": "lint-staged",
|
"pre-commit": "lint-staged",
|
||||||
"commit-msg": "vant commit-lint"
|
"commit-msg": "vant-cli commit-lint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{ts,tsx,js,vue}": [
|
"*.{ts,tsx,js,vue}": [
|
||||||
"eslint",
|
"eslint --fix",
|
||||||
"git add"
|
"git add"
|
||||||
],
|
],
|
||||||
"*.{vue,css,less}": [
|
"*.{vue,css,less}": [
|
||||||
"stylelint",
|
"stylelint --fix",
|
||||||
"git add"
|
"git add"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -62,55 +60,9 @@
|
|||||||
"vue": ">= 2.5.22"
|
"vue": ">= 2.5.22"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.7.2",
|
"@vant/cli": "^2.0.0-beta.20",
|
||||||
"@babel/plugin-proposal-optional-chaining": "^7.6.0",
|
|
||||||
"@babel/plugin-syntax-jsx": "^7.2.0",
|
|
||||||
"@babel/plugin-transform-object-assign": "^7.2.0",
|
|
||||||
"@babel/plugin-transform-runtime": "^7.6.2",
|
|
||||||
"@babel/preset-env": "^7.7.1",
|
|
||||||
"@babel/preset-typescript": "^7.7.2",
|
|
||||||
"@types/jest": "^24.0.22",
|
|
||||||
"@vant/cli": "^1.0.6",
|
|
||||||
"@vant/doc": "^2.6.1",
|
|
||||||
"@vant/eslint-config": "^1.4.0",
|
|
||||||
"@vant/markdown-loader": "^2.3.0",
|
|
||||||
"@vant/markdown-vetur": "^1.0.0",
|
|
||||||
"@vant/stylelint-config": "^1.0.0",
|
|
||||||
"@vant/touch-emulator": "^1.1.0",
|
|
||||||
"@vue/babel-preset-jsx": "^1.1.1",
|
|
||||||
"@vue/test-utils": "^1.0.0-beta.29",
|
|
||||||
"autoprefixer": "^9.7.1",
|
|
||||||
"babel-jest": "^24.9.0",
|
|
||||||
"babel-loader": "^8.0.6",
|
|
||||||
"codecov": "^3.6.1",
|
|
||||||
"cross-env": "^6.0.3",
|
|
||||||
"css-loader": "^3.2.0",
|
|
||||||
"csso": "^4.0.2",
|
|
||||||
"dependency-tree": "^7.0.2",
|
|
||||||
"eslint": "^6.6.0",
|
|
||||||
"fast-glob": "^3.1.0",
|
|
||||||
"gh-pages": "2.1.1",
|
|
||||||
"html-webpack-plugin": "3.2.0",
|
|
||||||
"jest": "^24.9.0",
|
|
||||||
"jest-serializer-vue": "^2.0.2",
|
|
||||||
"less": "^3.10.3",
|
|
||||||
"less-loader": "^5.0.0",
|
|
||||||
"lint-staged": "^9.4.2",
|
|
||||||
"postcss": "^7.0.21",
|
|
||||||
"postcss-loader": "^3.0.0",
|
|
||||||
"style-loader": "^1.0.0",
|
|
||||||
"stylelint": "^11.1.1",
|
|
||||||
"typescript": "^3.7.2",
|
|
||||||
"uppercamelcase": "^3.0.0",
|
|
||||||
"vue": "^2.6.10",
|
"vue": "^2.6.10",
|
||||||
"vue-jest": "4.0.0-beta.2",
|
"vue-template-compiler": "^2.6.10"
|
||||||
"vue-loader": "^15.7.2",
|
|
||||||
"vue-router": "^3.1.3",
|
|
||||||
"vue-template-compiler": "^2.6.10",
|
|
||||||
"webpack": "^4.41.2",
|
|
||||||
"webpack-cli": "^3.3.10",
|
|
||||||
"webpack-dev-server": "3.9.0",
|
|
||||||
"webpack-merge": "^4.2.2"
|
|
||||||
},
|
},
|
||||||
"sideEffects": [
|
"sideEffects": [
|
||||||
"es/**/style/*",
|
"es/**/style/*",
|
||||||
|
@ -1,33 +1,87 @@
|
|||||||
# Vant Cli
|
# Vant Cli
|
||||||
|
|
||||||
## Install
|
Vant Cli 是一个 Vue 组件库构建工具,通过 Vant Cli 可以快速搭建一套功能完备的 Vue 组件库。
|
||||||
|
|
||||||
#### NPM
|
### 特性
|
||||||
|
|
||||||
|
- 提供丰富的命令,涵盖从开发测试到构建发布的完整流程
|
||||||
|
- 基于约定的目录结构,自动生成优雅的文档站点和组件示例
|
||||||
|
- 内置 ESlint、Stylelint 校验规则,提交代码时自动执行校验
|
||||||
|
- 构建后的组件库默认支持按需引入、主题定制、Tree Shaking
|
||||||
|
|
||||||
|
### 安装
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
# 通过 npm 安装
|
||||||
npm i @vant/cli -D
|
npm i @vant/cli -D
|
||||||
```
|
|
||||||
|
|
||||||
#### YARN
|
# 通过 yarn 安装
|
||||||
|
|
||||||
```shell
|
|
||||||
yarn add @vant/cli --dev
|
yarn add @vant/cli --dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## Commands
|
安装完成后,请将以下配置添加到 package.json 文件中
|
||||||
|
|
||||||
#### Build Changelog
|
|
||||||
|
|
||||||
```shell
|
|
||||||
vant changelog ./name.md
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Commit Lint
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"husky": {
|
{
|
||||||
"hooks": {
|
"scripts": {
|
||||||
"commit-msg": "vant commit-lint"
|
"dev": "vant-cli dev",
|
||||||
}
|
"test": "vant-cli test",
|
||||||
|
"lint": "vant-cli lint",
|
||||||
|
"release": "vant-cli release",
|
||||||
|
"build-site": "vant-cli build-site"
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "lint-staged",
|
||||||
|
"commit-msg": "vant commit-lint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{ts,tsx,js,jsx,vue}": [
|
||||||
|
"eslint",
|
||||||
|
"git add"
|
||||||
|
],
|
||||||
|
"*.{vue,css,less,scss}": [
|
||||||
|
"stylelint",
|
||||||
|
"git add"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"extends": ["@vant"]
|
||||||
|
},
|
||||||
|
"stylelint": {
|
||||||
|
"extends": ["@vant/stylelint-config"]
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"singleQuote": true
|
||||||
|
},
|
||||||
|
"browserslist": ["Android >= 4.0", "iOS >= 7"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 命令
|
||||||
|
|
||||||
|
### dev
|
||||||
|
|
||||||
|
本地开发,dev 命令会启动一个本地服务器,用于在开发过程中对文档和示例进行预览
|
||||||
|
|
||||||
|
### build
|
||||||
|
|
||||||
|
构建组件库,在`es`和`lib`目录生成可用于生产环境的组件代码
|
||||||
|
|
||||||
|
### build-site
|
||||||
|
|
||||||
|
构建文档站点,在`site`目录生成可用于生产环境的文档站点代码
|
||||||
|
|
||||||
|
### release
|
||||||
|
|
||||||
|
发布组件库,发布前会自动执行 build 命令
|
||||||
|
|
||||||
|
### changelog
|
||||||
|
|
||||||
|
基于 Github 的 Pull Request 生成更新日志,仅对 Github 仓库有效
|
||||||
|
|
||||||
|
### commit-lint
|
||||||
|
|
||||||
|
校验 commit message 的格式是否符合规范,需要配合`husky`在提交 commit 时触发
|
||||||
|
@ -1,20 +1,126 @@
|
|||||||
{
|
{
|
||||||
"name": "@vant/cli",
|
"name": "@vant/cli",
|
||||||
"version": "1.0.6",
|
"version": "2.0.0-beta.20",
|
||||||
"description": "vant cli tools",
|
"description": "",
|
||||||
"main": "./src/index.js",
|
"main": "lib/index.js",
|
||||||
|
"typings": "lib/index.d.ts",
|
||||||
"bin": {
|
"bin": {
|
||||||
"vant": "./src/index.js"
|
"vant-cli": "./lib/index.js"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "tsc --watch",
|
||||||
|
"release": "tsc & release-it"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib",
|
||||||
|
"site",
|
||||||
|
"preset.js"
|
||||||
|
],
|
||||||
|
"author": "chenjiahan",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "https://github.com/youzan/vant/tree/dev/packages/vant-cli",
|
"peerDependencies": {
|
||||||
|
"vue": "^2.6.10",
|
||||||
|
"vue-template-compiler": "^2.6.10"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/csso": "^3.5.1",
|
||||||
|
"@types/eslint": "^6.1.3",
|
||||||
|
"@types/fs-extra": "^8.0.1",
|
||||||
|
"@types/html-webpack-plugin": "^3.2.1",
|
||||||
|
"@types/less": "^3.0.1",
|
||||||
|
"@types/lodash": "^4.14.149",
|
||||||
|
"@types/postcss-load-config": "^2.0.1",
|
||||||
|
"@types/sass": "^1.16.0",
|
||||||
|
"@types/shelljs": "^0.8.6",
|
||||||
|
"@types/signale": "^1.2.1",
|
||||||
|
"@types/source-map": "^0.5.7",
|
||||||
|
"@types/stylelint": "^9.10.1",
|
||||||
|
"@types/webpack": "^4.41.0",
|
||||||
|
"@types/webpack-dev-server": "^3.9.0",
|
||||||
|
"@types/webpack-merge": "^4.1.5"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"commander": "^2.17.1",
|
"@babel/core": "^7.7.4",
|
||||||
"husky": "^3.0.4",
|
"@babel/plugin-proposal-optional-chaining": "^7.7.4",
|
||||||
"shelljs": "^0.8.2",
|
"@babel/plugin-syntax-jsx": "^7.7.4",
|
||||||
"signale": "^1.4.0"
|
"@babel/plugin-transform-object-assign": "^7.7.4",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.7.4",
|
||||||
|
"@babel/preset-env": "^7.7.4",
|
||||||
|
"@babel/preset-typescript": "^7.7.4",
|
||||||
|
"@nuxt/friendly-errors-webpack-plugin": "^2.5.0",
|
||||||
|
"@types/jest": "^24.0.23",
|
||||||
|
"@vant/eslint-config": "^1.4.0",
|
||||||
|
"@vant/markdown-loader": "^2.3.0",
|
||||||
|
"@vant/markdown-vetur": "^1.0.0",
|
||||||
|
"@vant/stylelint-config": "^1.0.0",
|
||||||
|
"@vant/touch-emulator": "^1.2.0",
|
||||||
|
"@vue/babel-preset-jsx": "^1.1.2",
|
||||||
|
"@vue/component-compiler-utils": "^3.0.2",
|
||||||
|
"@vue/test-utils": "^1.0.0-beta.29",
|
||||||
|
"autoprefixer": "^9.7.2",
|
||||||
|
"babel-jest": "^24.9.0",
|
||||||
|
"babel-loader": "^8.0.6",
|
||||||
|
"babel-plugin-import": "^1.13.0",
|
||||||
|
"codecov": "^3.6.1",
|
||||||
|
"commander": "^4.0.1",
|
||||||
|
"cross-env": "^6.0.3",
|
||||||
|
"css-loader": "^3.2.0",
|
||||||
|
"csso": "^4.0.2",
|
||||||
|
"decamelize": "^3.2.0",
|
||||||
|
"eslint": "^6.7.1",
|
||||||
|
"find-babel-config": "^1.2.0",
|
||||||
|
"gh-pages": "2.0.1",
|
||||||
|
"html-webpack-plugin": "3.2.0",
|
||||||
|
"husky": "^3.1.0",
|
||||||
|
"jest": "^24.9.0",
|
||||||
|
"jest-serializer-vue": "^2.0.2",
|
||||||
|
"less": "^3.10.3",
|
||||||
|
"less-loader": "^5.0.0",
|
||||||
|
"lint-staged": "^9.5.0",
|
||||||
|
"lodash": "^4.17.15",
|
||||||
|
"portfinder": "^1.0.25",
|
||||||
|
"postcss": "^7.0.23",
|
||||||
|
"postcss-loader": "^3.0.0",
|
||||||
|
"release-it": "^12.4.3",
|
||||||
|
"sass": "^1.23.7",
|
||||||
|
"sass-loader": "^8.0.0",
|
||||||
|
"shelljs": "^0.8.3",
|
||||||
|
"signale": "^1.4.0",
|
||||||
|
"style-loader": "^1.0.1",
|
||||||
|
"stylelint": "^12.0.0",
|
||||||
|
"typescript": "^3.7.2",
|
||||||
|
"vue-jest": "4.0.0-beta.2",
|
||||||
|
"vue-loader": "^15.7.2",
|
||||||
|
"vue-router": "^3.1.3",
|
||||||
|
"webpack": "^4.41.2",
|
||||||
|
"webpack-dev-server": "3.9.0",
|
||||||
|
"webpack-merge": "^4.2.2"
|
||||||
|
},
|
||||||
|
"release-it": {
|
||||||
|
"git": {
|
||||||
|
"tag": false,
|
||||||
|
"commitMessage": "chore: Release @vant/cli@${version}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"extends": [
|
||||||
|
"@vant"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"global-require": 0,
|
||||||
|
"import/no-dynamic-require": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"stylelint": {
|
||||||
|
"extends": [
|
||||||
|
"@vant/stylelint-config"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"singleQuote": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
packages/vant-cli/preset.js
Normal file
3
packages/vant-cli/preset.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
const babelConfig = require('./lib/config/babel.config');
|
||||||
|
|
||||||
|
module.exports = () => babelConfig();
|
29
packages/vant-cli/site/common/iframe-router.js
Normal file
29
packages/vant-cli/site/common/iframe-router.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* 同步父窗口和 iframe 的 vue-router 状态
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { iframeReady, isMobile } from '.';
|
||||||
|
|
||||||
|
window.syncPath = function () {
|
||||||
|
const router = window.vueRouter;
|
||||||
|
const isInIframe = window !== window.top;
|
||||||
|
const currentDir = router.history.current.path;
|
||||||
|
|
||||||
|
if (isInIframe) {
|
||||||
|
window.top.replacePath(currentDir);
|
||||||
|
} else if (!isMobile) {
|
||||||
|
const iframe = document.querySelector('iframe');
|
||||||
|
if (iframe) {
|
||||||
|
iframeReady(iframe, () => {
|
||||||
|
iframe.contentWindow.replacePath(currentDir);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.replacePath = function (path = '') {
|
||||||
|
// should preserve hash for anchor
|
||||||
|
if (window.vueRouter.currentRoute.path !== path) {
|
||||||
|
window.vueRouter.replace(path).catch(() => {});
|
||||||
|
}
|
||||||
|
};
|
@ -1,7 +1,7 @@
|
|||||||
function iframeReady(iframe, callback) {
|
function iframeReady(iframe, callback) {
|
||||||
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
||||||
const interval = () => {
|
const interval = () => {
|
||||||
if (iframe.contentWindow.changePath) {
|
if (iframe.contentWindow.replacePath) {
|
||||||
callback();
|
callback();
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -20,14 +20,7 @@ function iframeReady(iframe, callback) {
|
|||||||
const ua = navigator.userAgent.toLowerCase();
|
const ua = navigator.userAgent.toLowerCase();
|
||||||
const isMobile = /ios|iphone|ipod|ipad|android/.test(ua);
|
const isMobile = /ios|iphone|ipod|ipad|android/.test(ua);
|
||||||
|
|
||||||
function importAll(map, r) {
|
|
||||||
r.keys().forEach(key => {
|
|
||||||
map[key] = r(key);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
isMobile,
|
isMobile,
|
||||||
importAll,
|
|
||||||
iframeReady
|
iframeReady
|
||||||
};
|
};
|
30
packages/vant-cli/site/common/locales.js
Normal file
30
packages/vant-cli/site/common/locales.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
const ZH_CN = 'zh-CN';
|
||||||
|
const EN_US = 'en-US';
|
||||||
|
const CACHE_KEY = 'vant-cli-lang';
|
||||||
|
|
||||||
|
let currentLang = ZH_CN;
|
||||||
|
|
||||||
|
export function getLang() {
|
||||||
|
return currentLang;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setLang(lang) {
|
||||||
|
currentLang = lang;
|
||||||
|
localStorage.setItem(CACHE_KEY, lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setDefaultLang(langFromConfig) {
|
||||||
|
const cached = localStorage.getItem(CACHE_KEY);
|
||||||
|
|
||||||
|
if (cached) {
|
||||||
|
currentLang = cached;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navigator.language && navigator.language.indexOf('zh-') !== -1) {
|
||||||
|
currentLang = ZH_CN;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentLang = langFromConfig || EN_US;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
@van-doc-black: #333;
|
@van-doc-black: #323233;
|
||||||
@van-doc-blue: #1989fa;
|
@van-doc-blue: #1989fa;
|
||||||
@van-doc-purple: #5758bb;
|
@van-doc-purple: #5758bb;
|
||||||
@van-doc-fuchsia: #a7419e;
|
@van-doc-fuchsia: #a7419e;
|
110
packages/vant-cli/site/desktop/App.vue
Normal file
110
packages/vant-cli/site/desktop/App.vue
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app">
|
||||||
|
<van-doc
|
||||||
|
:lang="lang"
|
||||||
|
:config="config"
|
||||||
|
:versions="versions"
|
||||||
|
:simulator="simulator"
|
||||||
|
:lang-configs="langConfigs"
|
||||||
|
>
|
||||||
|
<router-view />
|
||||||
|
</van-doc>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import VanDoc from './components';
|
||||||
|
import { config, packageVersion } from 'site-desktop-shared';
|
||||||
|
import { setLang } from '../common/locales';
|
||||||
|
|
||||||
|
function getPublicPath() {
|
||||||
|
const { site } = config.build || {};
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
return (site && site.publicPath) || '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
VanDoc
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
packageVersion,
|
||||||
|
simulator: `${getPublicPath()}mobile.html${location.hash}`
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
lang() {
|
||||||
|
const { lang } = this.$route.meta;
|
||||||
|
return lang || '';
|
||||||
|
},
|
||||||
|
|
||||||
|
langConfigs() {
|
||||||
|
const { locales = {} } = config.site;
|
||||||
|
return Object.keys(locales).map(key => ({
|
||||||
|
lang: key,
|
||||||
|
label: locales[key].langLabel || ''
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
config() {
|
||||||
|
const { locales } = config.site;
|
||||||
|
|
||||||
|
if (locales) {
|
||||||
|
return locales[this.lang];
|
||||||
|
}
|
||||||
|
|
||||||
|
return config.site;
|
||||||
|
},
|
||||||
|
|
||||||
|
versions() {
|
||||||
|
if (config.site.versions) {
|
||||||
|
return [{ label: packageVersion }, ...config.site.versions];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
lang(val) {
|
||||||
|
setLang(val);
|
||||||
|
this.setTitle();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.setTitle();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
setTitle() {
|
||||||
|
let { title } = this.config;
|
||||||
|
|
||||||
|
if (this.config.description) {
|
||||||
|
title += ` - ${this.config.description}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.title = title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.van-doc-intro {
|
||||||
|
padding-top: 20px;
|
||||||
|
font-family: 'Dosis', 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -18,7 +18,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
@import '../style/variable';
|
@import '../../common/style/index';
|
||||||
|
|
||||||
.van-doc-container {
|
.van-doc-container {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
@ -21,7 +21,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
@import '../style/variable';
|
@import '../../common/style/index';
|
||||||
|
|
||||||
.van-doc-content {
|
.van-doc-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -188,7 +188,7 @@ export default {
|
|||||||
blockquote {
|
blockquote {
|
||||||
margin: 20px 0 0;
|
margin: 20px 0 0;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
color: rgba(52, 73, 94, .8);
|
color: rgba(52, 73, 94, 0.8);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
background-color: #ecf9ff;
|
background-color: #ecf9ff;
|
||||||
border-left: 5px solid #50bfff;
|
border-left: 5px solid #50bfff;
|
@ -2,37 +2,39 @@
|
|||||||
<div class="van-doc-header">
|
<div class="van-doc-header">
|
||||||
<div class="van-doc-row">
|
<div class="van-doc-row">
|
||||||
<div class="van-doc-header__top">
|
<div class="van-doc-header__top">
|
||||||
<a class="van-doc-header__logo" :href="config.logo.href">
|
<a class="van-doc-header__logo">
|
||||||
<img :src="config.logo.image">
|
<img :src="config.logo">
|
||||||
<span>{{ config.logo.title }}</span>
|
<span>{{ config.title }}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<search-input v-if="searchConfig" :lang="lang" :search-config="searchConfig" />
|
<search-input v-if="searchConfig" :lang="lang" :search-config="searchConfig" />
|
||||||
|
|
||||||
<ul class="van-doc-header__top-nav">
|
<ul class="van-doc-header__top-nav">
|
||||||
<li v-for="item in config.nav.logoLink" class="van-doc-header__top-nav-item">
|
<li v-for="item in config.links" class="van-doc-header__top-nav-item">
|
||||||
<a class="van-doc-header__logo-link" target="_blank" :href="item.url">
|
<a class="van-doc-header__logo-link" target="_blank" :href="item.url">
|
||||||
<img :src="item.image">
|
<img :src="item.logo">
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li ref="version" v-if="versions" class="van-doc-header__top-nav-item">
|
<li ref="version" v-if="versions" class="van-doc-header__top-nav-item">
|
||||||
<span class="van-doc-header__cube van-doc-header__version" @click="toggleVersionPop">
|
<span class="van-doc-header__cube van-doc-header__version" @click="toggleVersionPop">
|
||||||
{{ versions[0] }}
|
{{ versions[0].label }}
|
||||||
<transition name="van-doc-dropdown">
|
<transition name="van-doc-dropdown">
|
||||||
<div v-if="showVersionPop" class="van-doc-header__version-pop">
|
<div v-if="showVersionPop" class="van-doc-header__version-pop">
|
||||||
<div
|
<div
|
||||||
v-for="item in versions"
|
v-for="item in versions"
|
||||||
class="van-doc-header__version-pop-item"
|
class="van-doc-header__version-pop-item"
|
||||||
@click="onSwitchVersion(item)"
|
@click="onSwitchVersion(item)"
|
||||||
>{{ item }}</div>
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li v-if="config.nav.lang" class="van-doc-header__top-nav-item">
|
<li v-if="langLabel && langLink" class="van-doc-header__top-nav-item">
|
||||||
<a class="van-doc-header__cube" :href="langLink">{{ config.nav.lang.text }}</a>
|
<a class="van-doc-header__cube" :href="langLink">{{ langLabel }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -53,9 +55,8 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
lang: String,
|
lang: String,
|
||||||
config: Object,
|
config: Object,
|
||||||
github: String,
|
|
||||||
versions: Array,
|
versions: Array,
|
||||||
searchConfig: Object
|
langConfigs: Array
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
@ -66,8 +67,24 @@ export default {
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
langLink() {
|
langLink() {
|
||||||
const { lang } = this.config.nav;
|
return `#${this.$route.path.replace(this.lang, this.anotherLang.lang)}`;
|
||||||
return `#${this.$route.path.replace(lang.from, lang.to)}`;
|
},
|
||||||
|
|
||||||
|
langLabel() {
|
||||||
|
return this.anotherLang.label;
|
||||||
|
},
|
||||||
|
|
||||||
|
anotherLang() {
|
||||||
|
const items = this.langConfigs.filter(item => item.lang !== this.lang);
|
||||||
|
if (items.length) {
|
||||||
|
return items[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
|
||||||
|
searchConfig() {
|
||||||
|
return this.config.searchConfig;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -92,14 +109,14 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onSwitchVersion(version) {
|
onSwitchVersion(version) {
|
||||||
this.$emit('switch-version', version);
|
location.href = version.link;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
@import '../style/variable';
|
@import '../../common/style/index';
|
||||||
|
|
||||||
.van-doc-header {
|
.van-doc-header {
|
||||||
width: 100%;
|
width: 100%;
|
@ -1,26 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="van-doc-nav" :style="style">
|
<div class="van-doc-nav" :style="style">
|
||||||
<div v-for="(item, index) in navConfig" class="van-doc-nav__item" :key="index">
|
<div
|
||||||
<van-doc-nav-link :item="item" :base="base" />
|
v-for="(group, index) in navConfig"
|
||||||
<div v-if="item.children">
|
class="van-doc-nav__group"
|
||||||
<div
|
:key="index"
|
||||||
class="nav-item van-doc-nav__group-title van-doc-nav__group-title"
|
>
|
||||||
v-for="(navItem, navIndex) in item.children"
|
<div class="van-doc-nav__title">
|
||||||
:key="navIndex"
|
{{ group.title }}
|
||||||
>
|
|
||||||
<van-doc-nav-link :item="navItem" :base="base" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<template v-if="item.groups">
|
<template v-if="group.items">
|
||||||
<div v-for="(group, groupIndex) in item.groups" :key="groupIndex">
|
<div
|
||||||
<div class="van-doc-nav__group-title">{{ group.groupName }}</div>
|
v-for="(item, groupIndex) in group.items"
|
||||||
<div>
|
:key="groupIndex"
|
||||||
<template v-for="(navItem, navItemIndex) in group.list">
|
class="van-doc-nav__item"
|
||||||
<div v-if="!navItem.disabled" :key="navItemIndex" class="van-doc-nav__subitem">
|
>
|
||||||
<van-doc-nav-link :item="navItem" :base="base" />
|
<van-doc-nav-link :item="item" :base="base" />
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@ -28,7 +22,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import NavLink from './NavLink.vue';
|
import NavLink from './NavLink';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'van-doc-nav',
|
name: 'van-doc-nav',
|
||||||
@ -38,11 +32,8 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
navConfig: Array,
|
lang: String,
|
||||||
base: {
|
navConfig: Array
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
@ -58,6 +49,10 @@ export default {
|
|||||||
top: this.top + 'px',
|
top: this.top + 'px',
|
||||||
bottom: this.bottom + 'px'
|
bottom: this.bottom + 'px'
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
base() {
|
||||||
|
return this.lang ? `/${this.lang}` : '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -76,7 +71,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
@import '../style/variable';
|
@import '../../common/style/index';
|
||||||
|
|
||||||
.van-doc-nav {
|
.van-doc-nav {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -86,10 +81,9 @@ export default {
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
min-width: @van-doc-nav-width;
|
min-width: @van-doc-nav-width;
|
||||||
max-width: @van-doc-nav-width;
|
max-width: @van-doc-nav-width;
|
||||||
padding: 25px 0 75px;
|
padding: 24px 0 72px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-right: 1px solid @van-doc-border-color;
|
|
||||||
box-shadow: 0 8px 12px #ebedf0;
|
box-shadow: 0 8px 12px #ebedf0;
|
||||||
|
|
||||||
@media (min-width: @van-doc-row-max-width) {
|
@media (min-width: @van-doc-row-max-width) {
|
||||||
@ -112,64 +106,46 @@ export default {
|
|||||||
background-color: rgba(69, 90, 100, 0.2);
|
background-color: rgba(69, 90, 100, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__item,
|
&__group {
|
||||||
&__subitem {
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
padding: 8px 0 8px @van-doc-padding;
|
||||||
|
color: #455a64;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item {
|
||||||
a {
|
a {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 10px 0 10px @van-doc-padding;
|
padding: 8px 0 8px @van-doc-padding;
|
||||||
color: #455a64;
|
color: #455a64;
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
line-height: 24px;
|
line-height: 28px;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: #000;
|
color: #000;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__item {
|
span {
|
||||||
> a {
|
font-size: 13px;
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__subitem {
|
|
||||||
a {
|
|
||||||
font-size: 14px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #000;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__group-title {
|
|
||||||
padding-left: @van-doc-padding;
|
|
||||||
color: @van-doc-text-light-blue;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 40px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1300px) {
|
@media (max-width: 1300px) {
|
||||||
min-width: 220px;
|
&__item {
|
||||||
max-width: 220px;
|
|
||||||
|
|
||||||
&__item,
|
|
||||||
&__subitem {
|
|
||||||
a {
|
|
||||||
line-height: 22px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__subitem {
|
|
||||||
a {
|
a {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
58
packages/vant-cli/site/desktop/components/NavLink.vue
Normal file
58
packages/vant-cli/site/desktop/components/NavLink.vue
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<router-link v-if="item.path" :class="{ active }" :to="path" v-html="itemName" />
|
||||||
|
<a v-else-if="item.link" :href="item.link" v-html="itemName" />
|
||||||
|
<a v-else v-html="itemName " />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'van-doc-nav-link',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
base: String,
|
||||||
|
item: Object
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
itemName() {
|
||||||
|
const name = (this.item.title || this.item.name).split(' ');
|
||||||
|
return `${name[0]} <span>${name.slice(1).join(' ')}</span>`;
|
||||||
|
},
|
||||||
|
|
||||||
|
path() {
|
||||||
|
const { path } = this.item;
|
||||||
|
return this.base ? `${this.base}/${path}` : path;
|
||||||
|
},
|
||||||
|
|
||||||
|
active() {
|
||||||
|
if (this.$route.path === this.path) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.item.path === 'home') {
|
||||||
|
return this.$route.path === this.base;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
active() {
|
||||||
|
this.scrollIntoView();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.scrollIntoView();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
scrollIntoView() {
|
||||||
|
if (this.active && this.$el && this.$el.scrollIntoViewIfNeeded) {
|
||||||
|
this.$el.scrollIntoViewIfNeeded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<input class="van-doc-search" :placeholder="searchPlaceholder">
|
<input class="van-doc-search" :placeholder="placeholder">
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -12,8 +12,8 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
searchPlaceholder() {
|
placeholder() {
|
||||||
return this.lang === 'zh-CN' ? '搜索文档...' : 'Search...';
|
return this.searchConfig.placeholder || 'Search...';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
@import '../style/variable';
|
@import '../../common/style/index';
|
||||||
|
|
||||||
.van-doc-search {
|
.van-doc-search {
|
||||||
width: 200px;
|
width: 200px;
|
@ -44,7 +44,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
@import '../style/variable';
|
@import '../../common/style/index';
|
||||||
|
|
||||||
.van-doc-simulator {
|
.van-doc-simulator {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -71,7 +71,7 @@ export default {
|
|||||||
|
|
||||||
@media (min-width: @van-doc-row-max-width) {
|
@media (min-width: @van-doc-row-max-width) {
|
||||||
right: 50%;
|
right: 50%;
|
||||||
margin-right: calc(-@van-doc-row-max-width / 2 + 40px);
|
margin-right: -@van-doc-row-max-width / 2 + 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-fixed {
|
&-fixed {
|
108
packages/vant-cli/site/desktop/components/index.vue
Normal file
108
packages/vant-cli/site/desktop/components/index.vue
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<template>
|
||||||
|
<div class="van-doc">
|
||||||
|
<doc-header
|
||||||
|
:lang="lang"
|
||||||
|
:config="config"
|
||||||
|
:versions="versions"
|
||||||
|
:lang-configs="langConfigs"
|
||||||
|
@switch-version="$emit('switch-version', $event)"
|
||||||
|
/>
|
||||||
|
<doc-nav :lang="lang" :nav-config="config.nav" />
|
||||||
|
<doc-container :has-simulator="!!simulator ">
|
||||||
|
<doc-content>
|
||||||
|
<slot />
|
||||||
|
</doc-content>
|
||||||
|
</doc-container>
|
||||||
|
<doc-simulator v-if="simulator" :src="simulator" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import DocNav from './Nav';
|
||||||
|
import DocHeader from './Header';
|
||||||
|
import DocContent from './Content';
|
||||||
|
import DocContainer from './Container';
|
||||||
|
import DocSimulator from './Simulator';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'van-doc',
|
||||||
|
|
||||||
|
components: {
|
||||||
|
DocNav,
|
||||||
|
DocHeader,
|
||||||
|
DocContent,
|
||||||
|
DocContainer,
|
||||||
|
DocSimulator
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
lang: String,
|
||||||
|
versions: Array,
|
||||||
|
simulator: String,
|
||||||
|
langConfigs: Array,
|
||||||
|
config: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
base: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
'$route.path'() {
|
||||||
|
this.setNav();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.setNav();
|
||||||
|
this.keyboardHandler();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
setNav() {
|
||||||
|
const { nav } = this.config;
|
||||||
|
const items = nav.reduce((list, item) => list.concat(item.items), []);
|
||||||
|
const currentPath = this.$route.path.split('/').pop();
|
||||||
|
|
||||||
|
let currentIndex;
|
||||||
|
for (let i = 0, len = items.length; i < len; i++) {
|
||||||
|
if (items[i].path === currentPath) {
|
||||||
|
currentIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.leftNav = items[currentIndex - 1];
|
||||||
|
this.rightNav = items[currentIndex + 1];
|
||||||
|
},
|
||||||
|
|
||||||
|
keyboardNav(direction) {
|
||||||
|
const nav = direction === 'prev' ? this.leftNav : this.rightNav;
|
||||||
|
if (nav.path) {
|
||||||
|
this.$router.push(this.base + nav.path);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
keyboardHandler() {
|
||||||
|
window.addEventListener('keyup', event => {
|
||||||
|
switch (event.keyCode) {
|
||||||
|
case 37: // left
|
||||||
|
this.keyboardNav('prev');
|
||||||
|
break;
|
||||||
|
case 39: // right
|
||||||
|
this.keyboardNav('next');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
@import '../../common/style/index';
|
||||||
|
</style>
|
17
packages/vant-cli/site/desktop/index.html
Normal file
17
packages/vant-cli/site/desktop/index.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
<link rel="icon" type="image/png" href="<%= htmlWebpackPlugin.options.logo %>" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
|
||||||
|
<meta http-Equiv="Cache-Control" Content="no-cache" />
|
||||||
|
<meta http-Equiv="Pragma" Content="no-cache" />
|
||||||
|
<meta http-Equiv="Expires" Content="0" />
|
||||||
|
<link href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" rel="stylesheet" />
|
||||||
|
</head>
|
||||||
|
<body ontouchstart>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
26
packages/vant-cli/site/desktop/main.js
Normal file
26
packages/vant-cli/site/desktop/main.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import Vue from 'vue';
|
||||||
|
import App from './App';
|
||||||
|
import { router } from './router';
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
Vue.config.productionTip = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
el: '#app',
|
||||||
|
mounted() {
|
||||||
|
if (this.$route.hash) {
|
||||||
|
// wait page init
|
||||||
|
setTimeout(() => {
|
||||||
|
const el = document.querySelector(this.$route.hash);
|
||||||
|
if (el) {
|
||||||
|
el.scrollIntoView({
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render: h => h(App),
|
||||||
|
router
|
||||||
|
});
|
109
packages/vant-cli/site/desktop/router.js
Normal file
109
packages/vant-cli/site/desktop/router.js
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import Vue from 'vue';
|
||||||
|
import VueRouter from 'vue-router';
|
||||||
|
import decamelize from 'decamelize';
|
||||||
|
import { isMobile } from '../common';
|
||||||
|
import { config, documents } from 'site-desktop-shared';
|
||||||
|
import { getLang, setDefaultLang } from '../common/locales';
|
||||||
|
import '../common/iframe-router';
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
location.replace('mobile.html' + location.hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { locales, defaultLang } = config.site;
|
||||||
|
|
||||||
|
setDefaultLang(defaultLang);
|
||||||
|
|
||||||
|
function parseName(name) {
|
||||||
|
if (name.indexOf('_') !== -1) {
|
||||||
|
const pairs = name.split('_');
|
||||||
|
const component = pairs.shift();
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: `${decamelize(component, '-')}`,
|
||||||
|
lang: pairs.join('-')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: `${decamelize(name, '-')}`,
|
||||||
|
lang: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLangFromRoute(route) {
|
||||||
|
const lang = route.path.split('/')[1];
|
||||||
|
const langs = Object.keys(locales);
|
||||||
|
|
||||||
|
if (langs.indexOf(lang) !== -1) {
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getLang();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRoutes() {
|
||||||
|
const routes = [];
|
||||||
|
const names = Object.keys(documents);
|
||||||
|
|
||||||
|
if (locales) {
|
||||||
|
routes.push({
|
||||||
|
path: '*',
|
||||||
|
redirect: route => `/${getLangFromRoute(route)}/`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
routes.push({
|
||||||
|
path: '*',
|
||||||
|
redirect: '/'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addHomeRoute(Home, lang) {
|
||||||
|
routes.push({
|
||||||
|
name: lang,
|
||||||
|
path: `/${lang || ''}`,
|
||||||
|
component: Home,
|
||||||
|
meta: { lang }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
names.forEach(name => {
|
||||||
|
const { component, lang } = parseName(name);
|
||||||
|
|
||||||
|
if (component === 'home') {
|
||||||
|
addHomeRoute(documents[name], lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
routes.push({
|
||||||
|
name: `${lang}/${component}`,
|
||||||
|
path: `/${lang}/${component}`,
|
||||||
|
component: documents[name],
|
||||||
|
meta: {
|
||||||
|
lang,
|
||||||
|
name: component
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
|
export const router = new VueRouter({
|
||||||
|
mode: 'hash',
|
||||||
|
routes: getRoutes(),
|
||||||
|
scrollBehavior(to) {
|
||||||
|
if (to.hash) {
|
||||||
|
return { selector: to.hash };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { x: 0, y: 0 };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.afterEach(() => {
|
||||||
|
Vue.nextTick(() => window.syncPath());
|
||||||
|
});
|
||||||
|
|
||||||
|
window.vueRouter = router;
|
32
packages/vant-cli/site/mobile/App.vue
Normal file
32
packages/vant-cli/site/mobile/App.vue
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<demo-nav />
|
||||||
|
<keep-alive>
|
||||||
|
<router-view />
|
||||||
|
</keep-alive>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import DemoNav from './components/DemoNav';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { DemoNav }
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
body {
|
||||||
|
min-width: 100vw;
|
||||||
|
color: #323233;
|
||||||
|
font-family: 'PingFang SC', Helvetica, Tohoma, Arial, sans-serif;
|
||||||
|
line-height: 1;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
</style>
|
12
packages/vant-cli/site/mobile/components/ArrowRight.vue
Normal file
12
packages/vant-cli/site/mobile/components/ArrowRight.vue
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<template>
|
||||||
|
<svg viewBox="0 0 1024 1024">
|
||||||
|
<path
|
||||||
|
fill="#B6C3D2"
|
||||||
|
d="M601.1 556.5L333.8 289.3c-24.5-24.5-24.5-64.6 0-89.1s64.6-24.5 89.1 0l267.3 267.3c24.5 24.5 24.5 64.6 0 89.1-24.5 24.4-64.6 24.4-89.1-.1z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#B6C3D2"
|
||||||
|
d="M690.2 556.5L422.9 823.8c-24.5 24.5-64.6 24.5-89.1 0s-24.5-64.6 0-89.1l267.3-267.3c24.5-24.5 64.6-24.5 89.1 0 24.5 24.6 24.5 64.6 0 89.1z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="van-doc-demo-block">
|
<div class="van-doc-demo-block">
|
||||||
<h2 class="van-doc-demo-block__title">{{ title }}</h2>
|
<h2 class="van-doc-demo-block__title">{{ title }}</h2>
|
||||||
<slot />
|
<slot />
|
||||||
</section>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -16,7 +16,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
@import '../style/variable';
|
@import '../../common/style/index';
|
||||||
|
|
||||||
.van-doc-demo-block {
|
.van-doc-demo-block {
|
||||||
&__title {
|
&__title {
|
89
packages/vant-cli/site/mobile/components/DemoHome.vue
Normal file
89
packages/vant-cli/site/mobile/components/DemoHome.vue
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<div class="demo-home">
|
||||||
|
<h1 class="demo-home__title">
|
||||||
|
<img :src="config.logo">
|
||||||
|
<span>{{ config.title }}</span>
|
||||||
|
</h1>
|
||||||
|
<h2 v-if="config.description" class="demo-home__desc">{{ config.description }}</h2>
|
||||||
|
<template v-for="(group, index) in config.nav">
|
||||||
|
<demo-home-nav
|
||||||
|
:group="group"
|
||||||
|
:lang="lang"
|
||||||
|
:key="index"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { config } from 'site-mobile-shared';
|
||||||
|
import DemoHomeNav from './DemoHomeNav';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
DemoHomeNav
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
lang() {
|
||||||
|
const { lang } = this.$route.meta;
|
||||||
|
return lang;
|
||||||
|
},
|
||||||
|
|
||||||
|
config() {
|
||||||
|
const { locales } = config.site;
|
||||||
|
|
||||||
|
if (locales) {
|
||||||
|
return locales[this.lang];
|
||||||
|
}
|
||||||
|
|
||||||
|
return config.site;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
@import '../../common/style/index';
|
||||||
|
|
||||||
|
.demo-home {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 46px 20px 20px;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
&__title,
|
||||||
|
&__desc {
|
||||||
|
padding-left: 16px;
|
||||||
|
font-weight: normal;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
margin: 0 0 16px;
|
||||||
|
|
||||||
|
img,
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin-left: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__desc {
|
||||||
|
margin: 0 0 40px;
|
||||||
|
color: rgba(69, 90, 100, 0.6);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
86
packages/vant-cli/site/mobile/components/DemoHomeNav.vue
Normal file
86
packages/vant-cli/site/mobile/components/DemoHomeNav.vue
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<div class="demo-home-nav">
|
||||||
|
<div class="demo-home-nav__title">{{ group.title }}</div>
|
||||||
|
<div class="demo-home-nav__group">
|
||||||
|
<router-link
|
||||||
|
class="demo-home-nav__block"
|
||||||
|
v-for="navItem in group.items"
|
||||||
|
:key="navItem.path"
|
||||||
|
:to="`${base}/${navItem.path}`"
|
||||||
|
>
|
||||||
|
{{ navItem.title }}
|
||||||
|
<arrow-right class="demo-home-nav__icon" />
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ArrowRight from './ArrowRight';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
ArrowRight
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
lang: String,
|
||||||
|
group: Object
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
active: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
base() {
|
||||||
|
return this.lang ? `/${this.lang}` : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
@import '../../common/style/index';
|
||||||
|
|
||||||
|
.demo-home-nav {
|
||||||
|
&__title {
|
||||||
|
margin: 24px 0 12px 16px;
|
||||||
|
color: rgba(69, 90, 100, 0.6);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__block {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
margin: 0 0 12px;
|
||||||
|
padding-left: 20px;
|
||||||
|
color: #323233;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 40px;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border-radius: 99px;
|
||||||
|
transition: background 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: darken(#f7f8fa, 3%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: darken(#f7f8fa, 6%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 16px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-top: -8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
58
packages/vant-cli/site/mobile/components/DemoNav.vue
Normal file
58
packages/vant-cli/site/mobile/components/DemoNav.vue
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<div v-show="title" class="demo-nav">
|
||||||
|
<div class="demo-nav__title">{{ title }}</div>
|
||||||
|
<svg class="demo-nav__back" viewBox="0 0 1000 1000" @click="onBack">
|
||||||
|
<path fill="#969799" fill-rule="evenodd" :d="path" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/* eslint-disable max-len */
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
path:
|
||||||
|
'M296.114 508.035c-3.22-13.597.473-28.499 11.079-39.105l333.912-333.912c16.271-16.272 42.653-16.272 58.925 0s16.272 42.654 0 58.926L395.504 498.47l304.574 304.574c16.272 16.272 16.272 42.654 0 58.926s-42.654 16.272-58.926 0L307.241 528.058a41.472 41.472 0 0 1-11.127-20.023z'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
title() {
|
||||||
|
const { name } = this.$route.meta || {};
|
||||||
|
return name ? name.replace(/-/g, '') : '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onBack() {
|
||||||
|
history.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.demo-nav {
|
||||||
|
position: relative;
|
||||||
|
height: 56px;
|
||||||
|
line-height: 56px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 17px;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__back {
|
||||||
|
position: absolute;
|
||||||
|
top: 16px;
|
||||||
|
left: 16px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -12,7 +12,7 @@ export default {
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
demoName() {
|
demoName() {
|
||||||
const { meta } = this.$route;
|
const { meta } = this.$route || {};
|
||||||
if (meta && meta.name) {
|
if (meta && meta.name) {
|
||||||
return `demo-${decamelize(meta.name, '-')}`;
|
return `demo-${decamelize(meta.name, '-')}`;
|
||||||
}
|
}
|
15
packages/vant-cli/site/mobile/index.html
Normal file
15
packages/vant-cli/site/mobile/index.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
<link rel="icon" type="image/png" href="<%= htmlWebpackPlugin.options.logo %>" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
|
||||||
|
<meta http-Equiv="Cache-Control" Content="no-cache" />
|
||||||
|
<meta http-Equiv="Pragma" Content="no-cache" />
|
||||||
|
<meta http-Equiv="Expires" Content="0" />
|
||||||
|
</head>
|
||||||
|
<body ontouchstart>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
21
packages/vant-cli/site/mobile/main.js
Normal file
21
packages/vant-cli/site/mobile/main.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import Vue from 'vue';
|
||||||
|
import DemoBlock from './components/DemoBlock';
|
||||||
|
import DemoSection from './components/DemoSection';
|
||||||
|
import { router } from './router';
|
||||||
|
import App from './App';
|
||||||
|
import '@vant/touch-emulator';
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
Vue.config.productionTip = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.component(DemoBlock.name, DemoBlock);
|
||||||
|
Vue.component(DemoSection.name, DemoSection);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
new Vue({
|
||||||
|
el: '#app',
|
||||||
|
render: h => h(App),
|
||||||
|
router
|
||||||
|
});
|
||||||
|
}, 0);
|
98
packages/vant-cli/site/mobile/router.js
Normal file
98
packages/vant-cli/site/mobile/router.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import Vue from 'vue';
|
||||||
|
import VueRouter from 'vue-router';
|
||||||
|
import decamelize from 'decamelize';
|
||||||
|
import DemoHome from './components/DemoHome';
|
||||||
|
import { demos, config } from 'site-mobile-shared';
|
||||||
|
import { getLang, setDefaultLang } from '../common/locales';
|
||||||
|
import '../common/iframe-router';
|
||||||
|
|
||||||
|
const { locales, defaultLang } = config.site;
|
||||||
|
|
||||||
|
setDefaultLang(defaultLang);
|
||||||
|
|
||||||
|
function getLangFromRoute(route) {
|
||||||
|
const lang = route.path.split('/')[1];
|
||||||
|
const langs = Object.keys(locales);
|
||||||
|
|
||||||
|
if (langs.indexOf(lang) !== -1) {
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getLang();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRoutes() {
|
||||||
|
const routes = [];
|
||||||
|
const names = Object.keys(demos);
|
||||||
|
const langs = locales ? Object.keys(locales) : [];
|
||||||
|
|
||||||
|
if (langs.length) {
|
||||||
|
routes.push({
|
||||||
|
path: '*',
|
||||||
|
redirect: route => `/${getLangFromRoute(route)}/`
|
||||||
|
});
|
||||||
|
|
||||||
|
langs.forEach(lang => {
|
||||||
|
routes.push({
|
||||||
|
path: `/${lang}`,
|
||||||
|
component: DemoHome,
|
||||||
|
meta: { lang }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
routes.push({
|
||||||
|
path: '*',
|
||||||
|
redirect: () => '/'
|
||||||
|
});
|
||||||
|
|
||||||
|
routes.push({
|
||||||
|
path: '',
|
||||||
|
component: DemoHome
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
names.forEach(name => {
|
||||||
|
const component = decamelize(name, '-');
|
||||||
|
|
||||||
|
if (langs.length) {
|
||||||
|
langs.forEach(lang => {
|
||||||
|
routes.push({
|
||||||
|
name: `${lang}/${component}`,
|
||||||
|
path: `/${lang}/${component}`,
|
||||||
|
component: demos[name],
|
||||||
|
meta: {
|
||||||
|
name,
|
||||||
|
lang
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
routes.push({
|
||||||
|
name,
|
||||||
|
path: `/${component}`,
|
||||||
|
component: demos[name],
|
||||||
|
meta: {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
|
export const router = new VueRouter({
|
||||||
|
mode: 'hash',
|
||||||
|
routes: getRoutes(),
|
||||||
|
scrollBehavior: (to, from, savedPosition) => savedPosition || { x: 0, y: 0 }
|
||||||
|
});
|
||||||
|
|
||||||
|
router.afterEach(() => {
|
||||||
|
if (!router.currentRoute.redirectedFrom) {
|
||||||
|
Vue.nextTick(window.syncPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.vueRouter = router;
|
7
packages/vant-cli/src/commands/build-site.ts
Normal file
7
packages/vant-cli/src/commands/build-site.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { setNodeEnv } from '../common';
|
||||||
|
import { compileSite } from '../compiler/compile-site';
|
||||||
|
|
||||||
|
export async function buildSite() {
|
||||||
|
setNodeEnv('production');
|
||||||
|
await compileSite(true);
|
||||||
|
}
|
150
packages/vant-cli/src/commands/build.ts
Normal file
150
packages/vant-cli/src/commands/build.ts
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import { join, relative } from 'path';
|
||||||
|
import { clean } from './clean';
|
||||||
|
import { remove, copy, readdirSync } from 'fs-extra';
|
||||||
|
import { compileJs } from '../compiler/compile-js';
|
||||||
|
import { compileSfc } from '../compiler/compile-sfc';
|
||||||
|
import { compileStyle } from '../compiler/compile-style';
|
||||||
|
import { compilePackage } from '../compiler/compile-package';
|
||||||
|
import { genPackageEntry } from '../compiler/gen-package-entry';
|
||||||
|
import { genStyleDepsMap } from '../compiler/gen-style-deps-map';
|
||||||
|
import { genComponentStyle } from '../compiler/gen-component-style';
|
||||||
|
import { SRC_DIR, LIB_DIR, ES_DIR } from '../common/constant';
|
||||||
|
import { getStepper } from '../common/logger';
|
||||||
|
import {
|
||||||
|
isDir,
|
||||||
|
isSfc,
|
||||||
|
isStyle,
|
||||||
|
isScript,
|
||||||
|
isDemoDir,
|
||||||
|
isTestDir,
|
||||||
|
setNodeEnv,
|
||||||
|
setModuleEnv
|
||||||
|
} from '../common';
|
||||||
|
import { genPacakgeStyle } from '../compiler/gen-package-style';
|
||||||
|
import { CSS_LANG } from '../common/css';
|
||||||
|
|
||||||
|
const stepper = getStepper(10);
|
||||||
|
|
||||||
|
async function compileDir(dir: string) {
|
||||||
|
const files = readdirSync(dir);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
files.map(filename => {
|
||||||
|
const filePath = join(dir, filename);
|
||||||
|
|
||||||
|
if (isDemoDir(filePath) || isTestDir(filePath)) {
|
||||||
|
return remove(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDir(filePath)) {
|
||||||
|
return compileDir(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSfc(filePath)) {
|
||||||
|
return compileSfc(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isScript(filePath)) {
|
||||||
|
return compileJs(filePath, { reloadConfig: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStyle(filePath)) {
|
||||||
|
return compileStyle(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return remove(filePath);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildESModuleOutputs() {
|
||||||
|
stepper.start('Build ESModule Outputs');
|
||||||
|
|
||||||
|
try {
|
||||||
|
setModuleEnv('esmodule');
|
||||||
|
await copy(SRC_DIR, ES_DIR);
|
||||||
|
await compileDir(ES_DIR);
|
||||||
|
stepper.success('Build ESModule Outputs');
|
||||||
|
} catch (err) {
|
||||||
|
stepper.error('Build ESModule Outputs', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildCommonjsOutputs() {
|
||||||
|
stepper.start('Build Commonjs Outputs');
|
||||||
|
|
||||||
|
try {
|
||||||
|
setModuleEnv('commonjs');
|
||||||
|
await copy(SRC_DIR, LIB_DIR);
|
||||||
|
await compileDir(LIB_DIR);
|
||||||
|
stepper.success('Build Commonjs Outputs');
|
||||||
|
} catch (err) {
|
||||||
|
stepper.error('Build Commonjs Outputs', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildStyleEntry() {
|
||||||
|
stepper.start('Build Style Entry');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await genStyleDepsMap();
|
||||||
|
genComponentStyle();
|
||||||
|
stepper.success('Build Style Entry');
|
||||||
|
} catch (err) {
|
||||||
|
stepper.error('Build Style Entry', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildPackedOutputs() {
|
||||||
|
stepper.start('Build Packed Outputs');
|
||||||
|
|
||||||
|
try {
|
||||||
|
setModuleEnv('esmodule');
|
||||||
|
await compilePackage(false);
|
||||||
|
await compilePackage(true);
|
||||||
|
stepper.success('Build Packed Outputs');
|
||||||
|
} catch (err) {
|
||||||
|
stepper.error('Build Packed Outputs', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildPackageEntry() {
|
||||||
|
stepper.start('Build Package Entry');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const esEntryFile = join(ES_DIR, 'index.js');
|
||||||
|
const libEntryFile = join(LIB_DIR, 'index.js');
|
||||||
|
const styleEntryFile = join(LIB_DIR, `index.${CSS_LANG}`);
|
||||||
|
|
||||||
|
genPackageEntry({
|
||||||
|
outputPath: esEntryFile,
|
||||||
|
pathResolver: (path: string) => `./${relative(SRC_DIR, path)}`
|
||||||
|
});
|
||||||
|
|
||||||
|
genPacakgeStyle({
|
||||||
|
outputPath: styleEntryFile,
|
||||||
|
pathResolver: (path: string) => path.replace(SRC_DIR, '.')
|
||||||
|
});
|
||||||
|
|
||||||
|
setModuleEnv('commonjs');
|
||||||
|
|
||||||
|
await copy(esEntryFile, libEntryFile);
|
||||||
|
await compileJs(libEntryFile, { reloadConfig: true });
|
||||||
|
await compileStyle(styleEntryFile);
|
||||||
|
|
||||||
|
stepper.success('Build Package Entry');
|
||||||
|
} catch (err) {
|
||||||
|
stepper.error('Build Package Entry', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function build() {
|
||||||
|
setNodeEnv('production');
|
||||||
|
|
||||||
|
await clean();
|
||||||
|
await buildESModuleOutputs();
|
||||||
|
await buildCommonjsOutputs();
|
||||||
|
await buildStyleEntry();
|
||||||
|
await buildPackageEntry();
|
||||||
|
await buildPackedOutputs();
|
||||||
|
}
|
@ -1,12 +1,12 @@
|
|||||||
const path = require('path');
|
import { join } from 'path';
|
||||||
const shelljs = require('shelljs');
|
import { exec } from 'shelljs';
|
||||||
|
import { ROOT } from '../common/constant';
|
||||||
|
|
||||||
function changelog(dist, cmd) {
|
export function changelog(dist: string, cmd: { tag?: string }) {
|
||||||
const basepath = process.cwd();
|
|
||||||
const tag = cmd.tag || 'v1.0.0';
|
const tag = cmd.tag || 'v1.0.0';
|
||||||
|
|
||||||
shelljs.exec(`
|
exec(`
|
||||||
basepath=${basepath}
|
basepath=${ROOT}
|
||||||
|
|
||||||
github_changelog_generator \
|
github_changelog_generator \
|
||||||
--header-label "# 更新日志" \
|
--header-label "# 更新日志" \
|
||||||
@ -18,9 +18,6 @@ function changelog(dist, cmd) {
|
|||||||
--no-author \
|
--no-author \
|
||||||
--no-unreleased \
|
--no-unreleased \
|
||||||
--since-tag ${tag} \
|
--since-tag ${tag} \
|
||||||
-o ${path.join(basepath, dist)}
|
-o ${join(ROOT, dist)}
|
||||||
`
|
`);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = changelog;
|
|
11
packages/vant-cli/src/commands/clean.ts
Normal file
11
packages/vant-cli/src/commands/clean.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { emptyDir } from 'fs-extra';
|
||||||
|
import { ES_DIR, LIB_DIR, DIST_DIR, SITE_DIST_DIR } from '../common/constant';
|
||||||
|
|
||||||
|
export async function clean() {
|
||||||
|
await Promise.all([
|
||||||
|
emptyDir(ES_DIR),
|
||||||
|
emptyDir(LIB_DIR),
|
||||||
|
emptyDir(DIST_DIR),
|
||||||
|
emptyDir(SITE_DIST_DIR)
|
||||||
|
]);
|
||||||
|
}
|
@ -1,14 +1,15 @@
|
|||||||
const fs = require('fs');
|
import { readFileSync } from 'fs-extra';
|
||||||
const signale = require('signale');
|
import { logger } from '../common/logger';
|
||||||
|
|
||||||
const commitRE = /^(revert: )?(fix|feat|docs|perf|test|types|build|chore|refactor|breaking change)(\(.+\))?: .{1,50}/;
|
const commitRE = /^(revert: )?(fix|feat|docs|perf|test|types|build|chore|refactor|breaking change)(\(.+\))?: .{1,50}/;
|
||||||
|
const mergeRE = /Merge branch /;
|
||||||
|
|
||||||
function commitLint() {
|
export function commitLint() {
|
||||||
const gitParams = process.env.HUSKY_GIT_PARAMS;
|
const gitParams = process.env.HUSKY_GIT_PARAMS as string;
|
||||||
const commitMsg = fs.readFileSync(gitParams, 'utf-8').trim();
|
const commitMsg = readFileSync(gitParams, 'utf-8').trim();
|
||||||
|
|
||||||
if (!commitRE.test(commitMsg)) {
|
if (!commitRE.test(commitMsg) && !mergeRE.test(commitMsg)) {
|
||||||
signale.error(`Error: invalid commit message: "${commitMsg}".
|
logger.error(`Error: invalid commit message: "${commitMsg}".
|
||||||
|
|
||||||
Proper commit message format is required for automated changelog generation.
|
Proper commit message format is required for automated changelog generation.
|
||||||
|
|
||||||
@ -30,9 +31,8 @@ Allowed Types:
|
|||||||
- chore
|
- chore
|
||||||
- refactor
|
- refactor
|
||||||
- breaking change
|
- breaking change
|
||||||
|
- Merge branch 'foo' into 'bar'
|
||||||
`);
|
`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = commitLint;
|
|
9
packages/vant-cli/src/commands/dev.ts
Normal file
9
packages/vant-cli/src/commands/dev.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { clean } from '../commands/clean';
|
||||||
|
import { setNodeEnv } from '../common';
|
||||||
|
import { compileSite } from '../compiler/compile-site';
|
||||||
|
|
||||||
|
export async function dev() {
|
||||||
|
setNodeEnv('development');
|
||||||
|
await clean();
|
||||||
|
await compileSite();
|
||||||
|
}
|
20
packages/vant-cli/src/commands/jest.ts
Normal file
20
packages/vant-cli/src/commands/jest.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { runCLI } from 'jest';
|
||||||
|
import { setNodeEnv } from '../common';
|
||||||
|
import { genPackageEntry } from '../compiler/gen-package-entry';
|
||||||
|
import { ROOT, JEST_CONFIG_FILE, PACKAGE_ENTRY_FILE } from '../common/constant';
|
||||||
|
|
||||||
|
export function test(command: any) {
|
||||||
|
setNodeEnv('test');
|
||||||
|
|
||||||
|
genPackageEntry({
|
||||||
|
outputPath: PACKAGE_ENTRY_FILE
|
||||||
|
});
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
rootDir: ROOT,
|
||||||
|
watch: command.watch,
|
||||||
|
config: JEST_CONFIG_FILE
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
runCLI(config, [ROOT]);
|
||||||
|
}
|
49
packages/vant-cli/src/commands/lint.ts
Normal file
49
packages/vant-cli/src/commands/lint.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { lint as stylelint } from 'stylelint';
|
||||||
|
import { CLIEngine } from 'eslint';
|
||||||
|
import { getStepper } from '../common/logger';
|
||||||
|
import { SCRIPT_EXTS } from '../common/constant';
|
||||||
|
|
||||||
|
const stepper = getStepper(4);
|
||||||
|
|
||||||
|
function lintScript() {
|
||||||
|
stepper.start('ESLint Start');
|
||||||
|
|
||||||
|
const cli = new CLIEngine({
|
||||||
|
fix: true,
|
||||||
|
extensions: SCRIPT_EXTS
|
||||||
|
});
|
||||||
|
|
||||||
|
const report = cli.executeOnFiles(['src/']);
|
||||||
|
const formatter = cli.getFormatter();
|
||||||
|
|
||||||
|
CLIEngine.outputFixes(report);
|
||||||
|
|
||||||
|
// output lint errors
|
||||||
|
const formatted = formatter(report.results);
|
||||||
|
if (formatted) {
|
||||||
|
stepper.error('ESLint Failed', '\n' + formatter(report.results));
|
||||||
|
} else {
|
||||||
|
stepper.success('ESLint Passed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function lintStyle() {
|
||||||
|
stepper.start('Stylelint Start');
|
||||||
|
|
||||||
|
stylelint({
|
||||||
|
fix: true,
|
||||||
|
formatter: 'string',
|
||||||
|
files: ['src/**/*.css', 'src/**/*.less', 'src/**/*.scss', 'src/**/*.vue']
|
||||||
|
}).then(result => {
|
||||||
|
if (result.errored) {
|
||||||
|
stepper.error('Stylelint Failed', '\n' + result.output);
|
||||||
|
} else {
|
||||||
|
stepper.success('Stylelint Passed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function lint() {
|
||||||
|
lintScript();
|
||||||
|
lintStyle();
|
||||||
|
}
|
14
packages/vant-cli/src/commands/release.ts
Normal file
14
packages/vant-cli/src/commands/release.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/* eslint-disable no-template-curly-in-string */
|
||||||
|
// @ts-ignore
|
||||||
|
import releaseIt from 'release-it';
|
||||||
|
import { build } from './build';
|
||||||
|
|
||||||
|
export async function release() {
|
||||||
|
await build();
|
||||||
|
await releaseIt({
|
||||||
|
git: {
|
||||||
|
tagName: 'v${version}',
|
||||||
|
commitMessage: 'chore: release ${version}'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
71
packages/vant-cli/src/common/constant.ts
Normal file
71
packages/vant-cli/src/common/constant.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { get } from 'lodash';
|
||||||
|
import { existsSync } from 'fs-extra';
|
||||||
|
import { join, dirname, isAbsolute } from 'path';
|
||||||
|
|
||||||
|
function findRootDir(dir: string): string {
|
||||||
|
if (dir === '/') {
|
||||||
|
return '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existsSync(join(dir, 'vant.config.js'))) {
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
return findRootDir(dirname(dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CWD = process.cwd();
|
||||||
|
export const ROOT = findRootDir(CWD);
|
||||||
|
export const ES_DIR = join(ROOT, 'es');
|
||||||
|
export const LIB_DIR = join(ROOT, 'lib');
|
||||||
|
export const DOCS_DIR = join(ROOT, 'docs');
|
||||||
|
export const SITE_DIST_DIR = join(ROOT, 'site');
|
||||||
|
export const VANT_CONFIG_FILE = join(ROOT, 'vant.config.js');
|
||||||
|
export const PACKAGE_JSON_FILE = join(ROOT, 'package.json');
|
||||||
|
export const WEBPACK_CONFIG_FILE = join(ROOT, 'webpack.config.js');
|
||||||
|
|
||||||
|
export const DIST_DIR = join(__dirname, '../../dist');
|
||||||
|
export const CONFIG_DIR = join(__dirname, '../config');
|
||||||
|
|
||||||
|
export const PACKAGE_ENTRY_FILE = join(DIST_DIR, 'package-entry.js');
|
||||||
|
export const PACKAGE_STYLE_FILE = join(DIST_DIR, 'package-style.css');
|
||||||
|
export const SITE_MODILE_SHARED_FILE = join(DIST_DIR, 'site-mobile-shared.js');
|
||||||
|
export const SITE_DESKTOP_SHARED_FILE = join(DIST_DIR, 'site-desktop-shared.js');
|
||||||
|
export const STYPE_DEPS_JSON_FILE = join(DIST_DIR, 'style-deps.json');
|
||||||
|
|
||||||
|
export const BABEL_CONFIG_FILE = join(CONFIG_DIR, 'babel.config.js');
|
||||||
|
export const POSTCSS_CONFIG_FILE = join(CONFIG_DIR, 'postcss.config.js');
|
||||||
|
export const JEST_INIT_FILE = join(CONFIG_DIR, 'jest.init.js');
|
||||||
|
export const JEST_CONFIG_FILE = join(CONFIG_DIR, 'jest.config.js');
|
||||||
|
export const JEST_TRANSFORM_FILE = join(CONFIG_DIR, 'jest.transform.js');
|
||||||
|
export const JEST_FILE_MOCK_FILE = join(CONFIG_DIR, 'jest.file-mock.js');
|
||||||
|
export const JEST_STYLE_MOCK_FILE = join(CONFIG_DIR, 'jest.style-mock.js');
|
||||||
|
|
||||||
|
export const SCRIPT_EXTS = ['.js', '.jsx', '.vue', '.ts', '.tsx'];
|
||||||
|
export const STYLE_EXTS = ['.css', '.less', '.scss'];
|
||||||
|
|
||||||
|
export const PACKAGE_JSON = require(PACKAGE_JSON_FILE);
|
||||||
|
|
||||||
|
export function getVantConfig() {
|
||||||
|
delete require.cache[VANT_CONFIG_FILE];
|
||||||
|
|
||||||
|
return require(VANT_CONFIG_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSrcDir() {
|
||||||
|
const vantConfig = getVantConfig();
|
||||||
|
const srcDir = get(vantConfig, 'build.srcDir');
|
||||||
|
|
||||||
|
if (srcDir) {
|
||||||
|
if (isAbsolute(srcDir)) {
|
||||||
|
return srcDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
return join(ROOT, srcDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
return join(ROOT, 'src');
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SRC_DIR = getSrcDir();
|
||||||
|
export const STYLE_DIR = join(SRC_DIR, 'style');
|
36
packages/vant-cli/src/common/css.ts
Normal file
36
packages/vant-cli/src/common/css.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { get } from 'lodash';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
|
import { join, isAbsolute } from 'path';
|
||||||
|
import { getVantConfig } from '../common';
|
||||||
|
import { STYLE_DIR, SRC_DIR } from './constant';
|
||||||
|
|
||||||
|
type CSS_LANG = 'css' | 'less' | 'scss';
|
||||||
|
|
||||||
|
function getCssLang(): CSS_LANG {
|
||||||
|
const vantConfig = getVantConfig();
|
||||||
|
const preprocessor = get(vantConfig, 'build.css.preprocessor', 'less');
|
||||||
|
|
||||||
|
if (preprocessor === 'sass') {
|
||||||
|
return 'scss';
|
||||||
|
}
|
||||||
|
|
||||||
|
return preprocessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CSS_LANG = getCssLang();
|
||||||
|
|
||||||
|
export function getCssBaseFile() {
|
||||||
|
const vantConfig = getVantConfig();
|
||||||
|
let path = join(STYLE_DIR, `base.${CSS_LANG}`);
|
||||||
|
|
||||||
|
const baseFile = get(vantConfig, 'build.css.base', '');
|
||||||
|
if (baseFile) {
|
||||||
|
path = isAbsolute(baseFile) ? baseFile : join(SRC_DIR, baseFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existsSync(path)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
131
packages/vant-cli/src/common/index.ts
Normal file
131
packages/vant-cli/src/common/index.ts
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import decamelize from 'decamelize';
|
||||||
|
import { join } from 'path';
|
||||||
|
import {
|
||||||
|
lstatSync,
|
||||||
|
existsSync,
|
||||||
|
readdirSync,
|
||||||
|
readFileSync,
|
||||||
|
outputFileSync
|
||||||
|
} from 'fs-extra';
|
||||||
|
import { SRC_DIR, getVantConfig, WEBPACK_CONFIG_FILE } from './constant';
|
||||||
|
|
||||||
|
export const EXT_REGEXP = /\.\w+$/;
|
||||||
|
export const SFC_REGEXP = /\.(vue)$/;
|
||||||
|
export const DEMO_REGEXP = /\/demo$/;
|
||||||
|
export const TEST_REGEXP = /\/test$/;
|
||||||
|
export const STYLE_REGEXP = /\.(css|less|scss)$/;
|
||||||
|
export const SCRIPT_REGEXP = /\.(js|ts|jsx|tsx)$/;
|
||||||
|
export const ENTRY_EXTS = ['js', 'ts', 'tsx', 'jsx', 'vue'];
|
||||||
|
|
||||||
|
export function removeExt(path: string) {
|
||||||
|
return path.replace('.js', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function replaceExt(path: string, ext: string) {
|
||||||
|
return path.replace(EXT_REGEXP, ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getComponents() {
|
||||||
|
const EXCLUDES = ['.DS_Store'];
|
||||||
|
const dirs = readdirSync(SRC_DIR);
|
||||||
|
return dirs
|
||||||
|
.filter(dir => !EXCLUDES.includes(dir))
|
||||||
|
.filter(dir =>
|
||||||
|
ENTRY_EXTS.some(ext => {
|
||||||
|
const path = join(SRC_DIR, dir, `index.${ext}`);
|
||||||
|
if (existsSync(path)) {
|
||||||
|
return readFileSync(path, 'utf-8').indexOf('export default') !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDir(dir: string) {
|
||||||
|
return lstatSync(dir).isDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDemoDir(dir: string) {
|
||||||
|
return DEMO_REGEXP.test(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isTestDir(dir: string) {
|
||||||
|
return TEST_REGEXP.test(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSfc(path: string) {
|
||||||
|
return SFC_REGEXP.test(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isStyle(path: string) {
|
||||||
|
return STYLE_REGEXP.test(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isScript(path: string) {
|
||||||
|
return SCRIPT_REGEXP.test(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
const camelizeRE = /-(\w)/g;
|
||||||
|
const pascalizeRE = /(\w)(\w*)/g;
|
||||||
|
|
||||||
|
export function camelize(str: string): string {
|
||||||
|
return str.replace(camelizeRE, (_, c) => c.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pascalize(str: string): string {
|
||||||
|
return camelize(str).replace(
|
||||||
|
pascalizeRE,
|
||||||
|
(_, c1, c2) => c1.toUpperCase() + c2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWebpackConfig(): object {
|
||||||
|
if (existsSync(WEBPACK_CONFIG_FILE)) {
|
||||||
|
const config = require(WEBPACK_CONFIG_FILE);
|
||||||
|
|
||||||
|
if (typeof config === 'function') {
|
||||||
|
return config();
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModuleEnv = 'esmodule' | 'commonjs';
|
||||||
|
export type NodeEnv = 'production' | 'development' | 'test';
|
||||||
|
export type BuildTarget = 'site' | 'package';
|
||||||
|
|
||||||
|
export function setModuleEnv(value: ModuleEnv) {
|
||||||
|
process.env.BABEL_MODULE = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setNodeEnv(value: NodeEnv) {
|
||||||
|
process.env.NODE_ENV = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setBuildTarget(value: BuildTarget) {
|
||||||
|
process.env.BUILD_TARGET = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDev() {
|
||||||
|
return process.env.NODE_ENV === 'development';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smarter outputFileSync
|
||||||
|
// Skip if content unchanged
|
||||||
|
export function smartOutputFile(filePath: string, content: string) {
|
||||||
|
if (existsSync(filePath)) {
|
||||||
|
const previousContent = readFileSync(filePath, 'utf-8');
|
||||||
|
|
||||||
|
if (previousContent === content) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outputFileSync(filePath, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { decamelize, getVantConfig };
|
25
packages/vant-cli/src/common/logger.ts
Normal file
25
packages/vant-cli/src/common/logger.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import logger from 'signale';
|
||||||
|
|
||||||
|
logger.config({
|
||||||
|
displayTimestamp: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const methods = ['success', 'start', 'error'] as const;
|
||||||
|
|
||||||
|
type Stepper = Pick<typeof logger, typeof methods[number]>;
|
||||||
|
|
||||||
|
export function getStepper(totalStep: number) {
|
||||||
|
const stepper = {} as Stepper;
|
||||||
|
let currentStep = 0;
|
||||||
|
|
||||||
|
methods.forEach(key => {
|
||||||
|
stepper[key] = (message, ...args) => {
|
||||||
|
const prefix = `[${++currentStep}/${totalStep}] `;
|
||||||
|
return logger[key](prefix + message, ...args);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return stepper;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { logger };
|
13
packages/vant-cli/src/compiler/compile-css.ts
Normal file
13
packages/vant-cli/src/compiler/compile-css.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import postcss from 'postcss';
|
||||||
|
import postcssrc from 'postcss-load-config';
|
||||||
|
import { minify } from 'csso';
|
||||||
|
import { POSTCSS_CONFIG_FILE } from '../common/constant';
|
||||||
|
|
||||||
|
export async function compileCss(source: string | Buffer) {
|
||||||
|
const config = await postcssrc({}, POSTCSS_CONFIG_FILE);
|
||||||
|
const { css } = await postcss(config.plugins as any).process(source, {
|
||||||
|
from: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
return minify(css).css;
|
||||||
|
}
|
58
packages/vant-cli/src/compiler/compile-js.ts
Normal file
58
packages/vant-cli/src/compiler/compile-js.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import findBabelConfig from 'find-babel-config';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { transformFileAsync } from '@babel/core';
|
||||||
|
import { removeSync, outputFileSync, existsSync } from 'fs-extra';
|
||||||
|
import { replaceExt } from '../common';
|
||||||
|
import { ROOT, DIST_DIR } from '../common/constant';
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
// whether to fouce reload babel config
|
||||||
|
reloadConfig?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TEMP_BABEL_CONFIG = join(DIST_DIR, 'babel.config.js');
|
||||||
|
|
||||||
|
function genTempBabelConfig() {
|
||||||
|
const { config } = findBabelConfig.sync(ROOT);
|
||||||
|
const content = `module.exports = function (api) {
|
||||||
|
api.cache.never();
|
||||||
|
|
||||||
|
return ${JSON.stringify(config)}
|
||||||
|
};`;
|
||||||
|
|
||||||
|
outputFileSync(TEMP_BABEL_CONFIG, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBabelOptions(options: Options) {
|
||||||
|
if (options.reloadConfig) {
|
||||||
|
if (!existsSync(TEMP_BABEL_CONFIG)) {
|
||||||
|
genTempBabelConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
configFile: TEMP_BABEL_CONFIG
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compileJs(
|
||||||
|
filePath: string,
|
||||||
|
options: Options = {}
|
||||||
|
): Promise<undefined> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
transformFileAsync(filePath, getBabelOptions(options))
|
||||||
|
.then(result => {
|
||||||
|
if (result) {
|
||||||
|
const jsFilePath = replaceExt(filePath, '.js');
|
||||||
|
|
||||||
|
removeSync(filePath);
|
||||||
|
outputFileSync(jsFilePath, result.code);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
}
|
11
packages/vant-cli/src/compiler/compile-less.ts
Normal file
11
packages/vant-cli/src/compiler/compile-less.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { readFileSync } from 'fs-extra';
|
||||||
|
import { render as renderLess } from 'less';
|
||||||
|
|
||||||
|
export async function compileLess(filePath: string) {
|
||||||
|
const source = readFileSync(filePath, 'utf-8');
|
||||||
|
const { css } = await renderLess(source, {
|
||||||
|
filename: filePath
|
||||||
|
});
|
||||||
|
|
||||||
|
return css;
|
||||||
|
}
|
14
packages/vant-cli/src/compiler/compile-package.ts
Normal file
14
packages/vant-cli/src/compiler/compile-package.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import webpack from 'webpack';
|
||||||
|
import { packageConfig } from '../config/webpack.package';
|
||||||
|
|
||||||
|
export async function compilePackage(isMinify: boolean) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
webpack(packageConfig(isMinify), (err, stats) => {
|
||||||
|
if (err || stats.hasErrors()) {
|
||||||
|
reject();
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
6
packages/vant-cli/src/compiler/compile-sass.ts
Normal file
6
packages/vant-cli/src/compiler/compile-sass.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { renderSync as renderSass } from 'sass';
|
||||||
|
|
||||||
|
export async function compileSass(filePath: string) {
|
||||||
|
const { css } = renderSass({ file: filePath });
|
||||||
|
return css;
|
||||||
|
}
|
129
packages/vant-cli/src/compiler/compile-sfc.ts
Normal file
129
packages/vant-cli/src/compiler/compile-sfc.ts
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import * as compiler from 'vue-template-compiler';
|
||||||
|
import * as compileUtils from '@vue/component-compiler-utils';
|
||||||
|
import { parse } from 'path';
|
||||||
|
import { remove, writeFileSync, readFileSync } from 'fs-extra';
|
||||||
|
import { replaceExt } from '../common';
|
||||||
|
import { compileJs } from './compile-js';
|
||||||
|
import { compileStyle } from './compile-style';
|
||||||
|
|
||||||
|
const RENDER_FN = '__vue_render__';
|
||||||
|
const STATIC_RENDER_FN = '__vue_staticRenderFns__';
|
||||||
|
const EXPORT = 'export default {';
|
||||||
|
|
||||||
|
// trim some unused code
|
||||||
|
function trim(code: string) {
|
||||||
|
return code.replace(/\/\/\n/g, '').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSfcStylePath(filePath: string, ext: string, index: number) {
|
||||||
|
const number = index !== 0 ? `-${index + 1}` : '';
|
||||||
|
return replaceExt(filePath, `-sfc${number}.${ext}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// inject render fn to script
|
||||||
|
function injectRender(script: string, render: string) {
|
||||||
|
script = trim(script);
|
||||||
|
|
||||||
|
render = render
|
||||||
|
.replace('var render', `var ${RENDER_FN}`)
|
||||||
|
.replace('var staticRenderFns', `var ${STATIC_RENDER_FN}`);
|
||||||
|
|
||||||
|
return script.replace(
|
||||||
|
EXPORT,
|
||||||
|
`${render}\n${EXPORT}\n render: ${RENDER_FN},\n\n staticRenderFns: ${STATIC_RENDER_FN},\n`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function injectStyle(
|
||||||
|
script: string,
|
||||||
|
styles: compileUtils.SFCBlock[],
|
||||||
|
filePath: string
|
||||||
|
) {
|
||||||
|
if (styles.length) {
|
||||||
|
const imports = styles
|
||||||
|
.map((style, index) => {
|
||||||
|
const { base } = parse(getSfcStylePath(filePath, 'css', index));
|
||||||
|
return `import './${base}';`;
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
return script.replace(EXPORT, `${imports}\n\n${EXPORT}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
|
||||||
|
function compileTemplate(template: string) {
|
||||||
|
const result = compileUtils.compileTemplate({
|
||||||
|
compiler,
|
||||||
|
source: template,
|
||||||
|
isProduction: true
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
return result.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
type CompileSfcOptions = {
|
||||||
|
skipStyle?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function parseSfc(filePath: string) {
|
||||||
|
const source = readFileSync(filePath, 'utf-8');
|
||||||
|
|
||||||
|
const descriptor = compileUtils.parse({
|
||||||
|
source,
|
||||||
|
compiler,
|
||||||
|
needMap: false
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function compileSfc(
|
||||||
|
filePath: string,
|
||||||
|
options: CompileSfcOptions = {}
|
||||||
|
): Promise<any> {
|
||||||
|
const tasks = [remove(filePath)];
|
||||||
|
const jsFilePath = replaceExt(filePath, '.js');
|
||||||
|
const descriptor = parseSfc(filePath);
|
||||||
|
const { template, styles } = descriptor;
|
||||||
|
|
||||||
|
// compile js part
|
||||||
|
if (descriptor.script) {
|
||||||
|
tasks.push(
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
let script = descriptor.script!.content;
|
||||||
|
script = injectStyle(script, styles, filePath);
|
||||||
|
|
||||||
|
if (template) {
|
||||||
|
const render = compileTemplate(template.content);
|
||||||
|
script = injectRender(script, render);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(jsFilePath, script);
|
||||||
|
compileJs(jsFilePath)
|
||||||
|
.then(resolve)
|
||||||
|
.catch(reject);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile style part
|
||||||
|
if (!options.skipStyle) {
|
||||||
|
tasks.push(
|
||||||
|
...styles.map((style, index: number) => {
|
||||||
|
const cssFilePath = getSfcStylePath(
|
||||||
|
filePath,
|
||||||
|
style.lang || 'css',
|
||||||
|
index
|
||||||
|
);
|
||||||
|
|
||||||
|
writeFileSync(cssFilePath, trim(style.content));
|
||||||
|
|
||||||
|
return compileStyle(cssFilePath);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(tasks);
|
||||||
|
}
|
50
packages/vant-cli/src/compiler/compile-site.ts
Normal file
50
packages/vant-cli/src/compiler/compile-site.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import webpack from 'webpack';
|
||||||
|
import WebpackDevServer from 'webpack-dev-server';
|
||||||
|
import { getPort } from 'portfinder';
|
||||||
|
import { siteDevConfig } from '../config/webpack.site.dev';
|
||||||
|
import { sitePrdConfig } from '../config/webpack.site.prd';
|
||||||
|
|
||||||
|
function watch() {
|
||||||
|
const server = new WebpackDevServer(
|
||||||
|
webpack(siteDevConfig),
|
||||||
|
siteDevConfig.devServer
|
||||||
|
);
|
||||||
|
|
||||||
|
getPort(
|
||||||
|
{
|
||||||
|
port: 8080
|
||||||
|
},
|
||||||
|
(err, port) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.listen(port, 'localhost', (err?: Error) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function build() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
webpack(sitePrdConfig, (err, stats) => {
|
||||||
|
if (err || stats.hasErrors()) {
|
||||||
|
reject();
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function compileSite(production = false) {
|
||||||
|
if (production) {
|
||||||
|
await build();
|
||||||
|
} else {
|
||||||
|
watch();
|
||||||
|
}
|
||||||
|
}
|
29
packages/vant-cli/src/compiler/compile-style.ts
Normal file
29
packages/vant-cli/src/compiler/compile-style.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { parse } from 'path';
|
||||||
|
import { readFileSync, writeFileSync } from 'fs';
|
||||||
|
import { replaceExt } from '../common';
|
||||||
|
import { compileCss } from './compile-css';
|
||||||
|
import { compileLess } from './compile-less';
|
||||||
|
import { compileSass } from './compile-sass';
|
||||||
|
|
||||||
|
async function compileFile(filePath: string) {
|
||||||
|
const parsedPath = parse(filePath);
|
||||||
|
|
||||||
|
if (parsedPath.ext === '.less') {
|
||||||
|
const source = await compileLess(filePath);
|
||||||
|
return compileCss(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedPath.ext === '.scss') {
|
||||||
|
const source = await compileSass(filePath);
|
||||||
|
return compileCss(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
const source = readFileSync(filePath, 'utf-8');
|
||||||
|
return compileCss(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function compileStyle(filePath: string) {
|
||||||
|
const css = await compileFile(filePath);
|
||||||
|
|
||||||
|
writeFileSync(replaceExt(filePath, '.css'), css);
|
||||||
|
}
|
94
packages/vant-cli/src/compiler/gen-component-style.ts
Normal file
94
packages/vant-cli/src/compiler/gen-component-style.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/**
|
||||||
|
* Build style entry of all components
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { join, relative } from 'path';
|
||||||
|
import { outputFileSync } from 'fs-extra';
|
||||||
|
import { getComponents, replaceExt } from '../common';
|
||||||
|
import { CSS_LANG, getCssBaseFile } from '../common/css';
|
||||||
|
import {
|
||||||
|
ES_DIR,
|
||||||
|
SRC_DIR,
|
||||||
|
LIB_DIR,
|
||||||
|
STYPE_DEPS_JSON_FILE
|
||||||
|
} from '../common/constant';
|
||||||
|
|
||||||
|
function getDeps(component: string): string[] {
|
||||||
|
const styleDepsJson = require(STYPE_DEPS_JSON_FILE);
|
||||||
|
|
||||||
|
if (styleDepsJson.map[component]) {
|
||||||
|
return [...styleDepsJson.map[component], component];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPath(component: string, ext = '.css') {
|
||||||
|
return join(ES_DIR, `${component}/index${ext}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRelativePath(component: string, style: string, ext: string) {
|
||||||
|
return relative(join(ES_DIR, `${component}/style`), getPath(style, ext));
|
||||||
|
}
|
||||||
|
|
||||||
|
const OUTPUT_CONFIG = [
|
||||||
|
{
|
||||||
|
dir: ES_DIR,
|
||||||
|
template: (dep: string) => `import '${dep}';`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dir: LIB_DIR,
|
||||||
|
template: (dep: string) => `require('${dep}');`
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function genEntry(params: {
|
||||||
|
ext: string;
|
||||||
|
filename: string;
|
||||||
|
component: string;
|
||||||
|
baseFile: string | null;
|
||||||
|
}) {
|
||||||
|
const { ext, filename, component, baseFile } = params;
|
||||||
|
const deps = getDeps(component);
|
||||||
|
const depsPath = deps.map(dep => getRelativePath(component, dep, ext));
|
||||||
|
|
||||||
|
OUTPUT_CONFIG.forEach(({ dir, template }) => {
|
||||||
|
const outputDir = join(dir, component, 'style');
|
||||||
|
const outputFile = join(outputDir, filename);
|
||||||
|
|
||||||
|
let content = '';
|
||||||
|
|
||||||
|
if (baseFile) {
|
||||||
|
const compiledBaseFile = replaceExt(baseFile.replace(SRC_DIR, dir), ext);
|
||||||
|
content += template(relative(outputDir, compiledBaseFile));
|
||||||
|
content += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
content += depsPath.map(template).join('\n');
|
||||||
|
|
||||||
|
outputFileSync(outputFile, content);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function genComponentStyle() {
|
||||||
|
const components = getComponents();
|
||||||
|
const baseFile = getCssBaseFile();
|
||||||
|
|
||||||
|
components.forEach(component => {
|
||||||
|
genEntry({
|
||||||
|
baseFile,
|
||||||
|
component,
|
||||||
|
filename: 'index.js',
|
||||||
|
ext: '.css'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (CSS_LANG !== 'css') {
|
||||||
|
genEntry({
|
||||||
|
baseFile,
|
||||||
|
component,
|
||||||
|
filename: CSS_LANG + '.js',
|
||||||
|
ext: '.' + CSS_LANG
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
71
packages/vant-cli/src/compiler/gen-package-entry.ts
Normal file
71
packages/vant-cli/src/compiler/gen-package-entry.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { get } from 'lodash';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { pascalize, getComponents, smartOutputFile } from '../common';
|
||||||
|
import { SRC_DIR, PACKAGE_JSON, getVantConfig } from '../common/constant';
|
||||||
|
|
||||||
|
const version = process.env.PACKAGE_VERSION || PACKAGE_JSON.version;
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
outputPath: string;
|
||||||
|
pathResolver?: Function;
|
||||||
|
};
|
||||||
|
|
||||||
|
function genImports(components: string[], options: Options): string {
|
||||||
|
return components
|
||||||
|
.map(name => {
|
||||||
|
let path = join(SRC_DIR, name);
|
||||||
|
if (options.pathResolver) {
|
||||||
|
path = options.pathResolver(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `import ${pascalize(name)} from '${path}';`;
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function genExports(names: string[]): string {
|
||||||
|
return names.map(name => `${name}`).join(',\n ');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function genPackageEntry(options: Options) {
|
||||||
|
const names = getComponents();
|
||||||
|
const vantConfig = getVantConfig();
|
||||||
|
const skipInstall = get(vantConfig, 'build.skipInstall', []).map(pascalize);
|
||||||
|
|
||||||
|
const components = names.map(pascalize);
|
||||||
|
const content = `${genImports(names, options)}
|
||||||
|
|
||||||
|
const version = '${version}';
|
||||||
|
|
||||||
|
function install(Vue) {
|
||||||
|
const components = [
|
||||||
|
${components.filter(item => !skipInstall.includes(item)).join(',\n ')}
|
||||||
|
];
|
||||||
|
|
||||||
|
components.forEach(item => {
|
||||||
|
if (item.install) {
|
||||||
|
Vue.use(item);
|
||||||
|
} else if (item.name) {
|
||||||
|
Vue.component(item.name, item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined' && window.Vue) {
|
||||||
|
install(window.Vue);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
install,
|
||||||
|
version,
|
||||||
|
${genExports(components)}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install,
|
||||||
|
version
|
||||||
|
};
|
||||||
|
`;
|
||||||
|
|
||||||
|
smartOutputFile(options.outputPath, content);
|
||||||
|
}
|
39
packages/vant-cli/src/compiler/gen-package-style.ts
Normal file
39
packages/vant-cli/src/compiler/gen-package-style.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { join } from 'path';
|
||||||
|
import { smartOutputFile } from '../common';
|
||||||
|
import { CSS_LANG, getCssBaseFile } from '../common/css';
|
||||||
|
import { SRC_DIR, STYPE_DEPS_JSON_FILE } from '../common/constant';
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
outputPath: string;
|
||||||
|
pathResolver?: Function;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function genPacakgeStyle(options: Options) {
|
||||||
|
const styleDepsJson = require(STYPE_DEPS_JSON_FILE);
|
||||||
|
const ext = '.' + CSS_LANG;
|
||||||
|
|
||||||
|
let content = '';
|
||||||
|
|
||||||
|
let baseFile = getCssBaseFile();
|
||||||
|
if (baseFile) {
|
||||||
|
if (options.pathResolver) {
|
||||||
|
baseFile = options.pathResolver(baseFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
content += `@import "${baseFile}";\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
content += styleDepsJson.sequence
|
||||||
|
.map((name: string) => {
|
||||||
|
let path = join(SRC_DIR, `${name}/index${ext}`);
|
||||||
|
|
||||||
|
if (options.pathResolver) {
|
||||||
|
path = options.pathResolver(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `@import "${path}";`;
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
smartOutputFile(options.outputPath, content);
|
||||||
|
}
|
115
packages/vant-cli/src/compiler/gen-site-desktop-shared.ts
Normal file
115
packages/vant-cli/src/compiler/gen-site-desktop-shared.ts
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import glob from 'fast-glob';
|
||||||
|
import { join, parse } from 'path';
|
||||||
|
import { existsSync, readdirSync } from 'fs-extra';
|
||||||
|
import {
|
||||||
|
pascalize,
|
||||||
|
removeExt,
|
||||||
|
getVantConfig,
|
||||||
|
smartOutputFile
|
||||||
|
} from '../common';
|
||||||
|
import {
|
||||||
|
SRC_DIR,
|
||||||
|
DOCS_DIR,
|
||||||
|
PACKAGE_JSON,
|
||||||
|
VANT_CONFIG_FILE,
|
||||||
|
SITE_DESKTOP_SHARED_FILE
|
||||||
|
} from '../common/constant';
|
||||||
|
|
||||||
|
type DocumentItem = {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function formatName(component: string, lang?: string) {
|
||||||
|
component = pascalize(component);
|
||||||
|
|
||||||
|
if (lang) {
|
||||||
|
return `${component}_${lang.replace('-', '_')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i18n mode:
|
||||||
|
* - action-sheet/README.md => ActionSheet_EnUS
|
||||||
|
* - action-sheet/README.zh-CN.md => ActionSheet_ZhCN
|
||||||
|
*
|
||||||
|
* default mode:
|
||||||
|
* - action-sheet/README.md => ActionSheet
|
||||||
|
*/
|
||||||
|
function resolveDocuments(components: string[]): DocumentItem[] {
|
||||||
|
const vantConfig = getVantConfig();
|
||||||
|
const { locales, defaultLang } = vantConfig.site;
|
||||||
|
|
||||||
|
const docs: DocumentItem[] = [];
|
||||||
|
|
||||||
|
if (locales) {
|
||||||
|
const langs = Object.keys(locales);
|
||||||
|
langs.forEach(lang => {
|
||||||
|
const fileName = lang === defaultLang ? 'README.md' : `README.${lang}.md`;
|
||||||
|
components.forEach(component => {
|
||||||
|
docs.push({
|
||||||
|
name: formatName(component, lang),
|
||||||
|
path: join(SRC_DIR, component, fileName)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
components.forEach(component => {
|
||||||
|
docs.push({
|
||||||
|
name: formatName(component),
|
||||||
|
path: join(SRC_DIR, component, 'README.md')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const staticDocs = glob.sync(join(DOCS_DIR, '**/*.md')).map(path => {
|
||||||
|
const pairs = parse(path).name.split('.');
|
||||||
|
return {
|
||||||
|
name: formatName(pairs[0], pairs[1] || defaultLang),
|
||||||
|
path
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return [...staticDocs, ...docs.filter(item => existsSync(item.path))];
|
||||||
|
}
|
||||||
|
|
||||||
|
function genImportDocuments(items: DocumentItem[]) {
|
||||||
|
return items
|
||||||
|
.map(item => `import ${item.name} from '${item.path}';`)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function genExportDocuments(items: DocumentItem[]) {
|
||||||
|
return `export const documents = {
|
||||||
|
${items.map(item => item.name).join(',\n ')}
|
||||||
|
};`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function genImportConfig() {
|
||||||
|
return `import config from '${removeExt(VANT_CONFIG_FILE)}';`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function genExportConfig() {
|
||||||
|
return 'export { config };';
|
||||||
|
}
|
||||||
|
|
||||||
|
function genExportVersion() {
|
||||||
|
return `export const packageVersion = '${PACKAGE_JSON.version}';`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function genSiteDesktopShared() {
|
||||||
|
const dirs = readdirSync(SRC_DIR);
|
||||||
|
const documents = resolveDocuments(dirs);
|
||||||
|
|
||||||
|
const code = `${genImportConfig()}
|
||||||
|
${genImportDocuments(documents)}
|
||||||
|
|
||||||
|
${genExportConfig()}
|
||||||
|
${genExportDocuments(documents)}
|
||||||
|
${genExportVersion()}
|
||||||
|
`;
|
||||||
|
|
||||||
|
smartOutputFile(SITE_DESKTOP_SHARED_FILE, code);
|
||||||
|
}
|
96
packages/vant-cli/src/compiler/gen-site-mobile-shared.ts
Normal file
96
packages/vant-cli/src/compiler/gen-site-mobile-shared.ts
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import { join } from 'path';
|
||||||
|
import { existsSync, readdirSync } from 'fs-extra';
|
||||||
|
import { SRC_DIR, SITE_MODILE_SHARED_FILE } from '../common/constant';
|
||||||
|
import {
|
||||||
|
pascalize,
|
||||||
|
removeExt,
|
||||||
|
decamelize,
|
||||||
|
getVantConfig,
|
||||||
|
smartOutputFile
|
||||||
|
} from '../common';
|
||||||
|
|
||||||
|
type DemoItem = {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
component: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function genInstall() {
|
||||||
|
return `import Vue from 'vue';
|
||||||
|
import PackageEntry from './package-entry';
|
||||||
|
import './package-style';
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function genImports(demos: DemoItem[]) {
|
||||||
|
return demos
|
||||||
|
.map(item => `import ${item.name} from '${removeExt(item.path)}';`)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function genExports(demos: DemoItem[]) {
|
||||||
|
return `export const demos = {\n ${demos
|
||||||
|
.map(item => item.name)
|
||||||
|
.join(',\n ')}\n};`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSetName(demos: DemoItem[]) {
|
||||||
|
return demos
|
||||||
|
.map(item => `${item.name}.name = 'demo-${item.component}';`)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function genConfig(demos: DemoItem[]) {
|
||||||
|
const vantConfig = getVantConfig();
|
||||||
|
const demoNames = demos.map(item => decamelize(item.name, '-'));
|
||||||
|
|
||||||
|
function demoFilter(nav: any[]) {
|
||||||
|
return nav.filter(group => {
|
||||||
|
group.items = group.items.filter((item: any) =>
|
||||||
|
demoNames.includes(item.path)
|
||||||
|
);
|
||||||
|
return group.items.length;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { nav, locales } = vantConfig.site;
|
||||||
|
if (locales) {
|
||||||
|
Object.keys(locales).forEach((lang: string) => {
|
||||||
|
if (locales[lang].nav) {
|
||||||
|
locales[lang].nav = demoFilter(locales[lang].nav);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (nav) {
|
||||||
|
vantConfig.site.nav = demoFilter(nav);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `export const config = ${JSON.stringify(vantConfig, null, 2)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function genCode(components: string[]) {
|
||||||
|
const demos = components
|
||||||
|
.map(component => ({
|
||||||
|
component,
|
||||||
|
name: pascalize(component),
|
||||||
|
path: join(SRC_DIR, component, 'demo/index.vue')
|
||||||
|
}))
|
||||||
|
.filter(item => existsSync(item.path));
|
||||||
|
|
||||||
|
return `${genInstall()}
|
||||||
|
${genImports(demos)}
|
||||||
|
|
||||||
|
Vue.use(PackageEntry);
|
||||||
|
|
||||||
|
${getSetName(demos)}
|
||||||
|
|
||||||
|
${genExports(demos)}
|
||||||
|
${genConfig(demos)}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function genSiteMobileShared() {
|
||||||
|
const dirs = readdirSync(SRC_DIR);
|
||||||
|
const code = genCode(dirs);
|
||||||
|
|
||||||
|
smartOutputFile(SITE_MODILE_SHARED_FILE, code);
|
||||||
|
}
|
121
packages/vant-cli/src/compiler/gen-style-deps-map.ts
Normal file
121
packages/vant-cli/src/compiler/gen-style-deps-map.ts
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import { join } from 'path';
|
||||||
|
import { CSS_LANG } from '../common/css';
|
||||||
|
import { existsSync } from 'fs-extra';
|
||||||
|
import { getDeps, clearDepsCache, fillExt } from './get-deps';
|
||||||
|
import { getComponents, smartOutputFile } from '../common';
|
||||||
|
import { SRC_DIR, STYPE_DEPS_JSON_FILE } from '../common/constant';
|
||||||
|
|
||||||
|
const components = getComponents();
|
||||||
|
|
||||||
|
function matchPath(path: string, component: string): boolean {
|
||||||
|
return path
|
||||||
|
.replace(SRC_DIR, '')
|
||||||
|
.split('/')
|
||||||
|
.includes(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStylePath(component: string) {
|
||||||
|
return join(SRC_DIR, `${component}/index.${CSS_LANG}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkStyleExists(component: string) {
|
||||||
|
return existsSync(getStylePath(component));
|
||||||
|
}
|
||||||
|
|
||||||
|
// analyze component dependencies
|
||||||
|
function analyzeComponentDeps(component: string) {
|
||||||
|
const checkList: string[] = [];
|
||||||
|
const componentEntry = fillExt(join(SRC_DIR, component, 'index'));
|
||||||
|
const record = new Set();
|
||||||
|
|
||||||
|
function search(filePath: string) {
|
||||||
|
record.add(filePath);
|
||||||
|
|
||||||
|
getDeps(filePath).forEach(key => {
|
||||||
|
if (record.has(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
search(key);
|
||||||
|
components
|
||||||
|
.filter(item => matchPath(key, item))
|
||||||
|
.forEach(item => {
|
||||||
|
if (!checkList.includes(item) && item !== component) {
|
||||||
|
checkList.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
search(componentEntry);
|
||||||
|
|
||||||
|
return checkList.filter(checkStyleExists);
|
||||||
|
}
|
||||||
|
|
||||||
|
type DepsMap = Record<string, string[]>;
|
||||||
|
|
||||||
|
function getSequence(depsMap: DepsMap) {
|
||||||
|
const sequence: string[] = [];
|
||||||
|
const record = new Set();
|
||||||
|
|
||||||
|
function add(item: string) {
|
||||||
|
const deps = depsMap[item];
|
||||||
|
|
||||||
|
if (sequence.includes(item) || !deps) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.has(item)) {
|
||||||
|
sequence.push(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
record.add(item);
|
||||||
|
|
||||||
|
if (!deps.length) {
|
||||||
|
sequence.push(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
deps.forEach(add);
|
||||||
|
|
||||||
|
if (sequence.includes(item)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxIndex = Math.max(...deps.map(dep => sequence.indexOf(dep)));
|
||||||
|
|
||||||
|
sequence.splice(maxIndex + 1, 0, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
components.forEach(add);
|
||||||
|
|
||||||
|
return sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function genStyleDepsMap() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
clearDepsCache();
|
||||||
|
|
||||||
|
const map = {} as DepsMap;
|
||||||
|
|
||||||
|
components.filter(checkStyleExists).forEach(component => {
|
||||||
|
map[component] = analyzeComponentDeps(component);
|
||||||
|
});
|
||||||
|
|
||||||
|
const sequence = getSequence(map);
|
||||||
|
|
||||||
|
Object.keys(map).forEach(key => {
|
||||||
|
map[key] = map[key].sort(
|
||||||
|
(a, b) => sequence.indexOf(a) - sequence.indexOf(b)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
smartOutputFile(
|
||||||
|
STYPE_DEPS_JSON_FILE,
|
||||||
|
JSON.stringify({ map, sequence }, null, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
73
packages/vant-cli/src/compiler/get-deps.ts
Normal file
73
packages/vant-cli/src/compiler/get-deps.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { join } from 'path';
|
||||||
|
import { SCRIPT_EXTS } from '../common/constant';
|
||||||
|
import { readFileSync, existsSync } from 'fs-extra';
|
||||||
|
|
||||||
|
let depsMap: Record<string, string[]> = {};
|
||||||
|
let existsCache: Record<string, boolean> = {};
|
||||||
|
|
||||||
|
// https://regexr.com/47jlq
|
||||||
|
const IMPORT_RE = /import\s+?(?:(?:(?:[\w*\s{},]*)\s+from\s+?)|)(?:(?:".*?")|(?:'.*?'))[\s]*?(?:;|$|)/g;
|
||||||
|
|
||||||
|
function matchImports(code: string): string[] {
|
||||||
|
return code.match(IMPORT_RE) || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function exists(filePath: string) {
|
||||||
|
if (!(filePath in existsCache)) {
|
||||||
|
existsCache[filePath] = existsSync(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return existsCache[filePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fillExt(filePath: string) {
|
||||||
|
for (let i = 0; i < SCRIPT_EXTS.length; i++) {
|
||||||
|
const completePath = `${filePath}${SCRIPT_EXTS[i]}`;
|
||||||
|
if (exists(completePath)) {
|
||||||
|
return completePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < SCRIPT_EXTS.length; i++) {
|
||||||
|
const completePath = `${filePath}/index${SCRIPT_EXTS[i]}`;
|
||||||
|
if (exists(completePath)) {
|
||||||
|
return completePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPathByImport(code: string, filePath: string) {
|
||||||
|
const divider = code.includes('"') ? '"' : "'";
|
||||||
|
const relativePath = code.split(divider)[1];
|
||||||
|
|
||||||
|
if (relativePath.includes('.')) {
|
||||||
|
return fillExt(join(filePath, '..', relativePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearDepsCache() {
|
||||||
|
depsMap = {};
|
||||||
|
existsCache = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDeps(filePath: string) {
|
||||||
|
if (depsMap[filePath]) {
|
||||||
|
return depsMap[filePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
const code = readFileSync(filePath, 'utf-8');
|
||||||
|
const imports = matchImports(code);
|
||||||
|
const paths = imports
|
||||||
|
.map(item => getPathByImport(item, filePath))
|
||||||
|
.filter(item => !!item) as string[];
|
||||||
|
|
||||||
|
depsMap[filePath] = paths;
|
||||||
|
|
||||||
|
paths.forEach(getDeps);
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
}
|
38
packages/vant-cli/src/compiler/vant-cli-site-plugin.ts
Normal file
38
packages/vant-cli/src/compiler/vant-cli-site-plugin.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { Compiler } from 'webpack';
|
||||||
|
import { replaceExt } from '../common';
|
||||||
|
import { CSS_LANG } from '../common/css';
|
||||||
|
import { genPackageEntry } from './gen-package-entry';
|
||||||
|
import { genPacakgeStyle } from './gen-package-style';
|
||||||
|
import { genSiteMobileShared } from './gen-site-mobile-shared';
|
||||||
|
import { genSiteDesktopShared } from './gen-site-desktop-shared';
|
||||||
|
import { genStyleDepsMap } from './gen-style-deps-map';
|
||||||
|
import { PACKAGE_ENTRY_FILE, PACKAGE_STYLE_FILE } from '../common/constant';
|
||||||
|
|
||||||
|
const PLUGIN_NAME = 'VantCliSitePlugin';
|
||||||
|
|
||||||
|
export class VantCliSitePlugin {
|
||||||
|
apply(compiler: Compiler) {
|
||||||
|
compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, this.genSiteEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
genSiteEntry() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
genStyleDepsMap()
|
||||||
|
.then(() => {
|
||||||
|
genPackageEntry({
|
||||||
|
outputPath: PACKAGE_ENTRY_FILE
|
||||||
|
});
|
||||||
|
genPacakgeStyle({
|
||||||
|
outputPath: replaceExt(PACKAGE_STYLE_FILE, `.${CSS_LANG}`)
|
||||||
|
});
|
||||||
|
genSiteMobileShared();
|
||||||
|
genSiteDesktopShared();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
48
packages/vant-cli/src/config/babel.config.ts
Normal file
48
packages/vant-cli/src/config/babel.config.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
module.exports = function() {
|
||||||
|
const { BABEL_MODULE, NODE_ENV } = process.env;
|
||||||
|
const isTest = NODE_ENV === 'test';
|
||||||
|
const useESModules = BABEL_MODULE !== 'commonjs' && !isTest;
|
||||||
|
|
||||||
|
return {
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@babel/preset-env',
|
||||||
|
{
|
||||||
|
loose: true,
|
||||||
|
modules: useESModules ? false : 'commonjs'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'@vue/babel-preset-jsx',
|
||||||
|
{
|
||||||
|
functional: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'@babel/preset-typescript'
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
[
|
||||||
|
'@babel/plugin-transform-runtime',
|
||||||
|
{
|
||||||
|
corejs: false,
|
||||||
|
helpers: true,
|
||||||
|
regenerator: isTest,
|
||||||
|
useESModules
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'import',
|
||||||
|
{
|
||||||
|
libraryName: 'vant',
|
||||||
|
libraryDirectory: 'es',
|
||||||
|
style: true
|
||||||
|
},
|
||||||
|
'vant'
|
||||||
|
],
|
||||||
|
'@babel/plugin-transform-object-assign',
|
||||||
|
'@babel/plugin-proposal-optional-chaining'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default module.exports;
|
28
packages/vant-cli/src/config/jest.config.ts
Normal file
28
packages/vant-cli/src/config/jest.config.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import {
|
||||||
|
JEST_INIT_FILE,
|
||||||
|
JEST_FILE_MOCK_FILE,
|
||||||
|
JEST_STYLE_MOCK_FILE
|
||||||
|
} from '../common/constant';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
moduleNameMapper: {
|
||||||
|
'\\.(css|less|scss)$': JEST_STYLE_MOCK_FILE,
|
||||||
|
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': JEST_FILE_MOCK_FILE
|
||||||
|
},
|
||||||
|
setupFiles: [JEST_INIT_FILE],
|
||||||
|
moduleFileExtensions: ['js', 'jsx', 'vue', 'ts', 'tsx'],
|
||||||
|
transform: {
|
||||||
|
'\\.(vue)$': 'vue-jest',
|
||||||
|
'\\.(js|jsx|ts|tsx)$': 'babel-jest'
|
||||||
|
},
|
||||||
|
transformIgnorePatterns: ['node_modules/(?!(vant|@babel\\/runtime)/)'],
|
||||||
|
snapshotSerializers: ['jest-serializer-vue'],
|
||||||
|
collectCoverageFrom: [
|
||||||
|
'src/**/*.{js,jsx,ts,tsx,vue}',
|
||||||
|
'!**/style/**',
|
||||||
|
'!**/demo/**'
|
||||||
|
],
|
||||||
|
collectCoverage: true,
|
||||||
|
coverageReporters: ['html', 'lcov', 'text-summary'],
|
||||||
|
coverageDirectory: './test/coverage'
|
||||||
|
};
|
1
packages/vant-cli/src/config/jest.file-mock.ts
Normal file
1
packages/vant-cli/src/config/jest.file-mock.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = 'test-file-stub';
|
5
packages/vant-cli/src/config/jest.init.ts
Normal file
5
packages/vant-cli/src/config/jest.init.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import Vue from 'vue';
|
||||||
|
// @ts-ignore
|
||||||
|
import Package from '../../dist/package-entry';
|
||||||
|
|
||||||
|
Vue.use(Package);
|
1
packages/vant-cli/src/config/jest.style-mock.ts
Normal file
1
packages/vant-cli/src/config/jest.style-mock.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = {};
|
97
packages/vant-cli/src/config/webpack.base.ts
Normal file
97
packages/vant-cli/src/config/webpack.base.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import FriendlyErrorsPlugin from '@nuxt/friendly-errors-webpack-plugin';
|
||||||
|
import sass from 'sass';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
import { VueLoaderPlugin } from 'vue-loader';
|
||||||
|
import {
|
||||||
|
ROOT,
|
||||||
|
STYLE_EXTS,
|
||||||
|
SCRIPT_EXTS,
|
||||||
|
POSTCSS_CONFIG_FILE
|
||||||
|
} from '../common/constant';
|
||||||
|
|
||||||
|
const CSS_LOADERS = [
|
||||||
|
'style-loader',
|
||||||
|
'css-loader',
|
||||||
|
{
|
||||||
|
loader: 'postcss-loader',
|
||||||
|
options: {
|
||||||
|
config: {
|
||||||
|
path: POSTCSS_CONFIG_FILE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const baseConfig = {
|
||||||
|
mode: 'development',
|
||||||
|
resolve: {
|
||||||
|
extensions: [...SCRIPT_EXTS, ...STYLE_EXTS]
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.vue$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'vue-loader',
|
||||||
|
options: {
|
||||||
|
compilerOptions: {
|
||||||
|
preserveWhitespace: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(js|ts|jsx|tsx)$/,
|
||||||
|
exclude: /node_modules\/(?!(@vant\/cli))/,
|
||||||
|
use: {
|
||||||
|
loader: 'babel-loader'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
sideEffects: true,
|
||||||
|
use: CSS_LOADERS
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.less$/,
|
||||||
|
sideEffects: true,
|
||||||
|
use: [
|
||||||
|
...CSS_LOADERS,
|
||||||
|
{
|
||||||
|
loader: 'less-loader',
|
||||||
|
options: {
|
||||||
|
paths: [resolve(ROOT, 'node_modules')]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.scss$/,
|
||||||
|
sideEffects: true,
|
||||||
|
use: [
|
||||||
|
...CSS_LOADERS,
|
||||||
|
{
|
||||||
|
loader: 'sass-loader',
|
||||||
|
options: {
|
||||||
|
implementation: sass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.md$/,
|
||||||
|
use: ['vue-loader', '@vant/markdown-loader']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new VueLoaderPlugin(),
|
||||||
|
new FriendlyErrorsPlugin({
|
||||||
|
clearConsole: false,
|
||||||
|
logLevel: 'WARNING'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
};
|
44
packages/vant-cli/src/config/webpack.package.ts
Normal file
44
packages/vant-cli/src/config/webpack.package.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import merge from 'webpack-merge';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { baseConfig } from './webpack.base';
|
||||||
|
import { getVantConfig, getWebpackConfig, setBuildTarget } from '../common';
|
||||||
|
import { LIB_DIR, ES_DIR } from '../common/constant';
|
||||||
|
|
||||||
|
export function packageConfig(isMinify: boolean) {
|
||||||
|
const { name } = getVantConfig();
|
||||||
|
|
||||||
|
setBuildTarget('package');
|
||||||
|
|
||||||
|
return merge(
|
||||||
|
baseConfig as any,
|
||||||
|
{
|
||||||
|
mode: 'production',
|
||||||
|
entry: {
|
||||||
|
[name]: join(ES_DIR, 'index.js')
|
||||||
|
},
|
||||||
|
stats: 'none',
|
||||||
|
output: {
|
||||||
|
path: LIB_DIR,
|
||||||
|
library: name,
|
||||||
|
libraryTarget: 'umd',
|
||||||
|
filename: isMinify ? '[name].min.js' : '[name].js',
|
||||||
|
umdNamedDefine: true,
|
||||||
|
// https://github.com/webpack/webpack/issues/6522
|
||||||
|
globalObject: "typeof self !== 'undefined' ? self : this"
|
||||||
|
},
|
||||||
|
externals: {
|
||||||
|
vue: {
|
||||||
|
root: 'Vue',
|
||||||
|
commonjs: 'vue',
|
||||||
|
commonjs2: 'vue',
|
||||||
|
amd: 'vue'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
performance: false,
|
||||||
|
optimization: {
|
||||||
|
minimize: isMinify
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getWebpackConfig()
|
||||||
|
);
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user