chore: add build scripts

This commit is contained in:
Benjy Cui 2016-02-29 14:27:11 +08:00
parent 45e9b304dc
commit fc86a4164d
7 changed files with 275 additions and 1 deletions

View File

@ -105,10 +105,12 @@
"less-loader": "^2.2.0", "less-loader": "^2.2.0",
"lesshint-antd": "^1.2.1", "lesshint-antd": "^1.2.1",
"lodash": "^4.1.0", "lodash": "^4.1.0",
"mark-twain": "^0.2.0-beta",
"nico-jsx": "~0.8.2", "nico-jsx": "~0.8.2",
"postcss-loader": "^0.8.0", "postcss-loader": "^0.8.0",
"pre-commit": "1.x", "pre-commit": "1.x",
"querystring": "^0.2.0", "querystring": "^0.2.0",
"ramda": "^0.19.1",
"rc-scroll-anim": "^0.1.7", "rc-scroll-anim": "^0.1.7",
"rc-tween-one": "^0.1.8", "rc-tween-one": "^0.1.8",
"react": "0.14.x", "react": "0.14.x",
@ -117,6 +119,7 @@
"react-dom": "0.14.x", "react-dom": "0.14.x",
"react-router": "^2.0.0", "react-router": "^2.0.0",
"react-stateless-wrapper": "~1.0.2", "react-stateless-wrapper": "~1.0.2",
"recast": "^0.11.2",
"reqwest": "~2.0.5", "reqwest": "~2.0.5",
"semver": "^5.1.0", "semver": "^5.1.0",
"values.js": "^1.0.3", "values.js": "^1.0.3",
@ -127,7 +130,7 @@
}, },
"scripts": { "scripts": {
"babel": "babel components index.js --out-dir lib", "babel": "babel components index.js --out-dir lib",
"start": "dora -p 8001 --plugins atool-build?config=webpack.website.config.js", "start": "npm run clean && ./scripts/build-website.js && dora -p 8001 --plugins atool-build?config=webpack.website.config.js",
"clean": "rm -rf _site dist", "clean": "rm -rf _site dist",
"site": "npm run clean && webpack --config webpack.deploy.config.js && webpack --config webpack.antd.config.js && NODE_ENV=PRODUCTION nico build", "site": "npm run clean && webpack --config webpack.deploy.config.js && webpack --config webpack.antd.config.js && NODE_ENV=PRODUCTION nico build",
"deploy": "rm -rf node_modules && node scripts/install.js && npm run just-deploy", "deploy": "rm -rf node_modules && node scripts/install.js && npm run just-deploy",

View File

@ -0,0 +1,20 @@
'use strict';
const fs = require('fs');
const R = require('ramda');
const utils = require('./utils');
module.exports = function buildComponentsList(indexes, outputPath) {
const componentMetas = R.map((fileName) => {
const fileContent = utils.parseFileContent(fileName);
return utils.parseMeta(fileContent);
}, indexes);
const groupByType = R.groupBy(R.compose(R.defaultTo('其它'), R.prop('type')));
const componentsList = groupByType(componentMetas);
const content = 'module.exports = ' +
JSON.stringify(componentsList, null, 2) + ';';
fs.writeFile(outputPath, content);
};

View File

@ -0,0 +1,70 @@
'use strict';
const fs = require('fs');
const path = require('path');
const R = require('ramda');
const devil = require('./devil'); // TODO: extract as a module?
const utils = require('./utils');
const stringify = function stringify(data, d) {
const depth = d || 1;
const indent = ' '.repeat(depth);
let output = '';
if (Array.isArray(data)) {
output += '[\n';
data.forEach((item) => output += indent + stringify(item, depth + 1) + ',\n');
output += indent + ']';
} else if (typeof data === 'object') {
output += '{\n';
for (const key of Object.keys(data)) {
output += indent + JSON.stringify(key) + ': ' + stringify(data[key], depth + 1) + ',\n';
}
output += indent + '}';
} else if (typeof data === 'function') {
output += data.toString();
} else if (typeof data === 'string') {
output += JSON.stringify(data);
} else {
output += data;
}
return output
.replace(/var _antd = require\(['"]antd['"]\);/, '')
.replace(/require\('antd\/lib/, 'require(\'../../components'); // TODO
};
const isIntro = function isIntro(element) {
const type = element.type;
return type !== 'h1' && type !== 'ul' && type !== 'code' && type !== 'hr';
};
const isCode = R.whereEq({ type: 'code', props: { lang: 'jsx' } });
const isStyle = R.whereEq({ type: 'code', props: { lang: 'css' } });
const getChildren = R.compose(R.prop('children'), R.defaultTo({}));
const sortByOrder = R.sortBy(R.prop('order'));
module.exports = function buildDemosList(demoList, outputPath) {
const demos = R.map((fileName) => {
const data = utils.parseFileContent(fileName);
const parts = fileName.split(path.sep);
const demo = {};
demo.order = parseInt(utils.parseMeta(data).order);
demo.parent = parts[parts.indexOf('components') + 1];
demo.id = 'components-' + demo.parent + '-demo-' + path.basename(fileName, '.md');
demo.title = data[0].children;
demo.intro = data.filter(isIntro);
demo.code = getChildren(data.find(isCode));
demo.preview = devil(demo.code);
demo.style = getChildren(data.find(isStyle));
return demo;
}, demoList);
const demosList = R.groupBy((demo) => demo.parent.replace('-', ''), demos);
const sortedDemosList = R.mapObjIndexed(sortByOrder, demosList);
const content = 'const React = require(\'react\');\n' +
'const ReactDOM = require(\'react-dom\');\n' +
'const _antd = require(\'../../\');\n' +
'module.exports = ' +
stringify(sortedDemosList, null, 2) + ';';
fs.writeFile(outputPath, content);
};

View File

@ -0,0 +1,26 @@
'use strict';
const fs = require('fs');
const R = require('ramda');
const utils = require('./utils');
const isMeta = R.complement(R.propEq('type', 'hr'));
const isDescription = R.complement(R.propEq('children', 'API'));
module.exports = function buildDocsList(indexes, outputPath) {
const indexesList = R.map((fileName) => {
const fileContent = utils.parseFileContent(fileName);
const meta = utils.parseMeta(fileContent);
const description = R.tail(R.dropWhile(
isMeta,
R.takeWhile(isDescription, fileContent)
));
const api = R.dropWhile(isDescription, fileContent);
return { meta, description, api };
}, indexes);
const content = 'module.exports = ' +
JSON.stringify(indexesList, null, 2) + ';';
fs.writeFile(outputPath, content);
};

77
scripts/build-website.js Executable file
View File

@ -0,0 +1,77 @@
#! /usr/bin/env node
'use strict';
const fs = require('fs');
const R = require('ramda');
const utils = require('./utils');
const buildComponentsList = require('./build-components-list');
const buildDocsList = require('./build-docs-list');
const buildDemosList = require('./build-demos-list');
// Ensure that data directory exist.
try {
fs.statSync('./_site');
} catch (e) {
fs.mkdir('./_site');
}
try {
fs.statSync('./_site/data');
} catch (e) {
fs.mkdir('./_site/data');
}
// TODO: configurable
const componentPath = './components';
const mds = utils.findMDFile(componentPath);
const indexes = R.filter(utils.isIndex, mds);
buildComponentsList(indexes, './_site/data/components-list.js');
buildDocsList(indexes, './_site/data/component-docs-list.js');
const demos = R.filter(utils.isDemo, mds);
buildDemosList(demos, './_site/data/demos-list.js');
const changelogPath = './CHANGELOG.md';
buildDocsList([changelogPath], './_site/data/changelog.js');
const introducePath = './docs/react/introduce.md';
buildDocsList([introducePath], './_site/data/introduce.js');
const gettingStartedPath = './docs/react/getting-started.md';
buildDocsList([gettingStartedPath], './_site/data/getting-started.js');
const installPath = './docs/react/install.md';
buildDocsList([installPath], './_site/data/install.js');
const upgradeNotesPath = './docs/react/upgrade-notes.md';
buildDocsList([upgradeNotesPath], './_site/data/upgrade-notes.js');
const downloadPath = './docs/resource/download.md';
buildDocsList([downloadPath], './_site/data/download.js')
const referencePath =
'./docs/resource/reference.md';
buildDocsList([referencePath], './_site/data/reference.js')
const specIntroPath =
'./docs/spec/introduce.md'
buildDocsList([specIntroPath], './_site/data/specIntro.js');
const fontPath =
'./docs/spec/font.md'
buildDocsList([fontPath], './_site/data/font.js');
const typographyPath =
'./docs/spec/typography.md'
buildDocsList([typographyPath], './_site/data/typography.js');
const easingPath =
'./docs/spec/easing.md'
buildDocsList([easingPath], './_site/data/easing.js');
const pageTransitionPath =
'./docs/spec/page-transition.md'
buildDocsList([pageTransitionPath], './_site/data/page-transition.js');
const motionPath =
'./docs/spec/motion.md'
buildDocsList([motionPath], './_site/data/motion.js');

31
scripts/devil.js Normal file
View File

@ -0,0 +1,31 @@
'use strict';
const babel = require('babel-core');
const recast = require('recast');
const builders = recast.types.builders;
const babelrc = {
presets: ['es2015', 'react']
};
// const demo = 'import { Button } from \'antd\';' +
// 'ReactDOM.render(<div>' +
// ' <Button type="primary">主按钮</Button>' +
// ' <Button>次按钮</Button>' +
// ' <Button type="ghost">幽灵按钮</Button>' +
// '</div>,' +
// 'document.getElementById(\'components-button-demo-basic\'));';
// devil(demo);
module.exports = function devil(demo, params) {
const compiled = babel.transform(demo, babelrc).code;
const ast = recast.parse(compiled);
const astProgramBody = ast.program.body;
const lastIndex = astProgramBody.length - 1;
astProgramBody[lastIndex] = builders.returnStatement(
astProgramBody[lastIndex].expression.arguments[0]
);
const code = recast.print(ast).code;
return new Function((params || []).join(', '), code);
}

47
scripts/utils.js Normal file
View File

@ -0,0 +1,47 @@
'use strict';
const fs = require('fs');
const path = require('path');
const R = require('ramda');
const isMDFile = R.compose(R.equals('.md'), path.extname);
exports.findMDFile = function findMDFile(dirPath) {
let mds = [];
R.forEach((fileName) => {
const filePath = path.join(dirPath, fileName);
const stat = fs.statSync(filePath);
if (stat.isFile() && isMDFile(filePath)) {
mds.push(filePath);
}
if (stat.isDirectory()) {
mds = mds.concat(findMDFile(filePath));
}
}, fs.readdirSync(dirPath));
return mds;
};
exports.isIndex = R.compose(R.equals('index.md'), R.unary(path.basename));
exports.isDemo = R.complement(exports.isIndex);
const MT = require('mark-twain');
exports.parseFileContent = R.pipe(
fs.readFileSync,
R.toString,
MT,
R.prop('content')
);
const parseBasicMeta = R.pipe(
R.path(['1', 'children']),
R.map((child) => R.split(/:\s?/, child.children[0].children)),
R.fromPairs
);
const parseEnglishTitle = R.path(['0', 'children']);
exports.parseMeta = function parseMeta(fileContent) {
const meta = parseBasicMeta(fileContent);
meta.english = parseEnglishTitle(fileContent);
meta.title = `${meta.english} ${meta.chinese || ''}`;
return meta;
};