Merge pull request #3542 from tschaub/examples
Generate example index and rebuild examples on source changes.
This commit is contained in:
@@ -1,5 +1,2 @@
|
||||
*.pyc
|
||||
/build/
|
||||
/examples/*.html.png
|
||||
/examples/example-list.js
|
||||
/examples/example-list.xml
|
||||
|
||||
1767
bin/BeautifulSoup.py
1767
bin/BeautifulSoup.py
File diff suppressed because it is too large
Load Diff
@@ -1,269 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from xml.dom.minidom import Document
|
||||
|
||||
try:
|
||||
import xml.etree.ElementTree as ElementTree
|
||||
except ImportError:
|
||||
try:
|
||||
import cElementTree as ElementTree # NOQA
|
||||
except ImportError:
|
||||
try:
|
||||
import elementtree.ElementTree as ElementTree # NOQA
|
||||
except ImportError:
|
||||
import lxml.etree as ElementTree # NOQA
|
||||
|
||||
missing_deps = False
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
try:
|
||||
import simplejson as json # NOQA
|
||||
except ImportError, E:
|
||||
missing_deps = E
|
||||
|
||||
try:
|
||||
from BeautifulSoup import BeautifulSoup
|
||||
except ImportError, E:
|
||||
missing_deps = E
|
||||
|
||||
feedName = "example-list.xml"
|
||||
feedPath = "http://openlayers.github.io/ol3/master/examples/"
|
||||
|
||||
|
||||
def getListOfExamples(relPath):
|
||||
"""
|
||||
returns list of .html filenames within a given path - excludes
|
||||
index.html
|
||||
"""
|
||||
examples = os.listdir(relPath)
|
||||
examples = [example for example in examples if
|
||||
example.endswith('.html') and example != "index.html"]
|
||||
return examples
|
||||
|
||||
|
||||
def getExampleHtml(path):
|
||||
"""
|
||||
returns html of a specific example
|
||||
"""
|
||||
print '.',
|
||||
f = open(path)
|
||||
html = f.read()
|
||||
f.close()
|
||||
return html
|
||||
|
||||
|
||||
def extractById(soup, tagId, value=None):
|
||||
"""
|
||||
returns full contents of a particular tag id
|
||||
"""
|
||||
beautifulTag = soup.find(id=tagId)
|
||||
if beautifulTag:
|
||||
if beautifulTag.contents:
|
||||
value = str(beautifulTag.renderContents()).strip()
|
||||
value = value.replace('\t', '')
|
||||
value = value.replace('\n', '')
|
||||
return value
|
||||
|
||||
|
||||
def getRelatedClasses(html):
|
||||
"""
|
||||
parses the html, and returns a list of all OpenLayers Classes
|
||||
used within (ie what parts of OL the javascript uses).
|
||||
"""
|
||||
rawstr = r'''(?P<class>ol\..*?)\('''
|
||||
return re.findall(rawstr, html)
|
||||
|
||||
|
||||
def parseHtml(html, ids):
|
||||
"""
|
||||
returns dictionary of items of interest
|
||||
"""
|
||||
soup = BeautifulSoup(html)
|
||||
d = {}
|
||||
for tagId in ids:
|
||||
d[tagId] = extractById(soup, tagId)
|
||||
#classes should eventually be parsed from docs - not automatically created.
|
||||
classes = getRelatedClasses(html)
|
||||
d['classes'] = classes
|
||||
return d
|
||||
|
||||
|
||||
def getGitInfo(exampleDir, exampleName):
|
||||
orig = os.getcwd()
|
||||
os.chdir(exampleDir)
|
||||
h = os.popen("git log -n 1 --pretty=format:'%an|%ai' " + exampleName)
|
||||
os.chdir(orig)
|
||||
log = h.read()
|
||||
h.close()
|
||||
d = {}
|
||||
if log:
|
||||
parts = log.split("|")
|
||||
d["author"] = parts[0]
|
||||
# compensate for spaces in git log time
|
||||
td = parts[1].split(" ")
|
||||
td.insert(1, "T")
|
||||
d["date"] = "".join(td)
|
||||
else:
|
||||
d["author"] = ""
|
||||
d["date"] = ""
|
||||
return d
|
||||
|
||||
|
||||
def createFeed(examples):
|
||||
doc = Document()
|
||||
atomuri = "http://www.w3.org/2005/Atom"
|
||||
feed = doc.createElementNS(atomuri, "feed")
|
||||
feed.setAttribute("xmlns", atomuri)
|
||||
title = doc.createElementNS(atomuri, "title")
|
||||
title.appendChild(doc.createTextNode("OpenLayers Examples"))
|
||||
feed.appendChild(title)
|
||||
link = doc.createElementNS(atomuri, "link")
|
||||
link.setAttribute("rel", "self")
|
||||
link.setAttribute("href", feedPath + feedName)
|
||||
|
||||
modtime = time.strftime("%Y-%m-%dT%I:%M:%SZ", time.gmtime())
|
||||
id = doc.createElementNS(atomuri, "id")
|
||||
id.appendChild(doc.createTextNode(
|
||||
"%s%s#%s" % (feedPath, feedName, modtime)))
|
||||
feed.appendChild(id)
|
||||
|
||||
updated = doc.createElementNS(atomuri, "updated")
|
||||
updated.appendChild(doc.createTextNode(modtime))
|
||||
feed.appendChild(updated)
|
||||
|
||||
examples.sort(key=lambda x: x["modified"])
|
||||
for example in sorted(examples, key=lambda x: x["modified"], reverse=True):
|
||||
entry = doc.createElementNS(atomuri, "entry")
|
||||
|
||||
title = doc.createElementNS(atomuri, "title")
|
||||
title.appendChild(doc.createTextNode(example["title"] or
|
||||
example["example"]))
|
||||
entry.appendChild(title)
|
||||
|
||||
tags = doc.createElementNS(atomuri, "tags")
|
||||
tags.appendChild(doc.createTextNode(example["tags"] or
|
||||
example["example"]))
|
||||
entry.appendChild(tags)
|
||||
|
||||
link = doc.createElementNS(atomuri, "link")
|
||||
link.setAttribute("href", "%s%s" % (feedPath, example["example"]))
|
||||
entry.appendChild(link)
|
||||
|
||||
summary = doc.createElementNS(atomuri, "summary")
|
||||
summary.appendChild(doc.createTextNode(example["shortdesc"] or
|
||||
example["example"]))
|
||||
entry.appendChild(summary)
|
||||
|
||||
updated = doc.createElementNS(atomuri, "updated")
|
||||
updated.appendChild(doc.createTextNode(example["modified"]))
|
||||
entry.appendChild(updated)
|
||||
|
||||
author = doc.createElementNS(atomuri, "author")
|
||||
name = doc.createElementNS(atomuri, "name")
|
||||
name.appendChild(doc.createTextNode(example["author"]))
|
||||
author.appendChild(name)
|
||||
entry.appendChild(author)
|
||||
|
||||
id = doc.createElementNS(atomuri, "id")
|
||||
id.appendChild(doc.createTextNode("%s%s#%s" % (feedPath,
|
||||
example["example"],
|
||||
example["modified"])))
|
||||
entry.appendChild(id)
|
||||
|
||||
feed.appendChild(entry)
|
||||
|
||||
doc.appendChild(feed)
|
||||
return doc
|
||||
|
||||
|
||||
def wordIndex(examples):
|
||||
"""
|
||||
Create an inverted index based on words in title and shortdesc. Keys are
|
||||
lower cased words. Values are dictionaries with example index keys and
|
||||
count values.
|
||||
"""
|
||||
index = {}
|
||||
unword = re.compile("\\W+")
|
||||
keys = ["shortdesc", "title", "tags"]
|
||||
for i in range(len(examples)):
|
||||
for key in keys:
|
||||
text = examples[i][key]
|
||||
if text:
|
||||
words = unword.split(text)
|
||||
for word in words:
|
||||
if word:
|
||||
word = word.lower()
|
||||
if word in index:
|
||||
if i in index[word]:
|
||||
index[word][i] += 1
|
||||
else:
|
||||
index[word][i] = 1
|
||||
else:
|
||||
index[word] = {i: 1}
|
||||
return index
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
if missing_deps:
|
||||
print """This script requires json or simplejson and BeautifulSoup.
|
||||
You don't have them. \n(%s)""" % E
|
||||
sys.exit()
|
||||
|
||||
if len(sys.argv) == 3:
|
||||
inExampleDir = sys.argv[1]
|
||||
outExampleDir = sys.argv[2]
|
||||
else:
|
||||
inExampleDir = "../examples"
|
||||
outExampleDir = "../examples"
|
||||
|
||||
outFile = open(os.path.join(outExampleDir, "example-list.js"), "w")
|
||||
|
||||
print 'Reading examples from %s and writing out to %s' % (inExampleDir,
|
||||
outFile.name)
|
||||
|
||||
exampleList = []
|
||||
docIds = ['title', 'shortdesc', 'tags']
|
||||
|
||||
examples = getListOfExamples(inExampleDir)
|
||||
|
||||
modtime = time.strftime("%Y-%m-%dT%I:%M:%SZ", time.gmtime())
|
||||
|
||||
for example in examples:
|
||||
path = os.path.join(inExampleDir, example)
|
||||
html = getExampleHtml(path)
|
||||
tagvalues = parseHtml(html, docIds)
|
||||
tagvalues['example'] = example
|
||||
# add in author/date info
|
||||
d = getGitInfo(inExampleDir, example)
|
||||
tagvalues["author"] = d["author"] or "anonymous"
|
||||
tagvalues["modified"] = d["date"] or modtime
|
||||
tagvalues['link'] = example
|
||||
|
||||
exampleList.append(tagvalues)
|
||||
|
||||
print
|
||||
|
||||
exampleList.sort(key=lambda x: x['example'].lower())
|
||||
|
||||
index = wordIndex(exampleList)
|
||||
|
||||
json = json.dumps({"examples": exampleList, "index": index})
|
||||
# Give the json a global variable we can use in our js.
|
||||
# This should be replaced or made optional.
|
||||
json = 'var info=' + json + ';'
|
||||
outFile.write(json)
|
||||
outFile.close()
|
||||
|
||||
outFeedPath = os.path.join(outExampleDir, feedName)
|
||||
print "writing feed to %s " % outFeedPath
|
||||
atom = open(outFeedPath, 'w')
|
||||
doc = createFeed(exampleList)
|
||||
atom.write(doc.toxml())
|
||||
atom.close()
|
||||
|
||||
print 'complete'
|
||||
72
build.py
72
build.py
@@ -112,23 +112,24 @@ EXECUTABLES = [variables.CLEANCSS, variables.GIT, variables.GJSLINT,
|
||||
variables.JSDOC, variables.JSHINT, variables.PYTHON,
|
||||
variables.PHANTOMJS]
|
||||
|
||||
EXAMPLES = [path
|
||||
for path in ifind('examples')
|
||||
if path.endswith('.html')
|
||||
if path != 'examples/index.html']
|
||||
EXAMPLES_SRC_ALL = [path for path in ifind('examples_src')]
|
||||
|
||||
EXAMPLES_SRC = [path
|
||||
for path in ifind('examples')
|
||||
if path.endswith('.js')
|
||||
if not path.endswith('.combined.js')
|
||||
if path != 'examples/Jugl.js'
|
||||
if path != 'examples/example-list.js']
|
||||
EXAMPLES_SRC_HTML = [path
|
||||
for path in EXAMPLES_SRC_ALL
|
||||
if path.endswith('.html')
|
||||
if path != 'examples_src/index.html']
|
||||
|
||||
EXAMPLES_SRC_JS = [example.replace('.html', '.js')
|
||||
for example in EXAMPLES_SRC_HTML]
|
||||
|
||||
EXAMPLES_DEST_ALL = [path.replace('examples_src', 'examples')
|
||||
for path in EXAMPLES_SRC_ALL]
|
||||
|
||||
EXAMPLES_JSON = ['build/' + example.replace('.html', '.json')
|
||||
for example in EXAMPLES]
|
||||
for example in EXAMPLES_SRC_HTML]
|
||||
|
||||
EXAMPLES_COMBINED = ['build/' + example.replace('.html', '.combined.js')
|
||||
for example in EXAMPLES]
|
||||
for example in EXAMPLES_SRC_HTML]
|
||||
|
||||
GLSL_SRC = [path
|
||||
for path in ifind('src')
|
||||
@@ -261,17 +262,15 @@ virtual('build-examples', 'examples', 'build/examples/all.combined.js',
|
||||
EXAMPLES_COMBINED)
|
||||
|
||||
|
||||
virtual('examples', 'examples/example-list.xml', EXAMPLES_JSON)
|
||||
virtual('examples', EXAMPLES_DEST_ALL)
|
||||
|
||||
|
||||
@target('examples/example-list.xml', 'examples/example-list.js')
|
||||
def examples_examples_list_xml(t):
|
||||
t.touch() # already generated by bin/exampleparser.py
|
||||
|
||||
|
||||
@target('examples/example-list.js', 'bin/exampleparser.py', EXAMPLES)
|
||||
def examples_examples_list_js(t):
|
||||
t.run('%(PYTHON)s', 'bin/exampleparser.py', 'examples', 'examples')
|
||||
@rule(r'\Aexamples/(?P<filepath>.*)\Z')
|
||||
def examples_dest(name, match):
|
||||
def action(t):
|
||||
t.run('node', 'tasks/build-examples.js')
|
||||
dependencies = ['examples_src/%(filepath)s' % match.groupdict()]
|
||||
return Target(name, action=action, dependencies=dependencies)
|
||||
|
||||
|
||||
@target('build/examples/all.combined.js', 'build/examples/all.js',
|
||||
@@ -282,7 +281,7 @@ def build_examples_all_combined_js(t):
|
||||
report_sizes(t)
|
||||
|
||||
|
||||
@target('build/examples/all.js', EXAMPLES_SRC)
|
||||
@target('build/examples/all.js', EXAMPLES_SRC_JS)
|
||||
def build_examples_all_js(t):
|
||||
t.output('%(PYTHON)s', 'bin/combine-examples.py', t.dependencies)
|
||||
|
||||
@@ -405,7 +404,7 @@ def examples_star_combined_js(name, match):
|
||||
return Target(name, action=action, dependencies=dependencies)
|
||||
|
||||
|
||||
@target('serve', 'examples', 'build/test_requires.js', 'build/test_rendering_requires.js',
|
||||
@target('serve', 'build/test_requires.js', 'build/test_rendering_requires.js',
|
||||
NPM_INSTALL)
|
||||
def serve(t):
|
||||
t.run('node', 'tasks/serve.js')
|
||||
@@ -415,7 +414,7 @@ virtual('lint', 'build/lint-timestamp', 'build/check-requires-timestamp',
|
||||
'build/check-whitespace-timestamp', 'jshint')
|
||||
|
||||
|
||||
@target('build/lint-timestamp', SRC, EXAMPLES_SRC, SPEC, SPEC_RENDERING,
|
||||
@target('build/lint-timestamp', SRC, EXAMPLES_SRC_JS, SPEC, SPEC_RENDERING,
|
||||
precious=True)
|
||||
def build_lint_src_timestamp(t):
|
||||
t.run('%(GJSLINT)s',
|
||||
@@ -427,7 +426,7 @@ def build_lint_src_timestamp(t):
|
||||
|
||||
virtual('jshint', 'build/jshint-timestamp')
|
||||
|
||||
@target('build/jshint-timestamp', SRC, EXAMPLES_SRC, SPEC, SPEC_RENDERING,
|
||||
@target('build/jshint-timestamp', SRC, EXAMPLES_SRC_JS, SPEC, SPEC_RENDERING,
|
||||
TASKS, NPM_INSTALL, precious=True)
|
||||
def build_jshint_timestamp(t):
|
||||
t.run(variables.JSHINT, '--verbose', t.newer(t.dependencies))
|
||||
@@ -457,7 +456,7 @@ def _strip_comments(lines):
|
||||
yield lineno, line
|
||||
|
||||
|
||||
@target('build/check-requires-timestamp', SRC, EXAMPLES_SRC, SHADER_SRC,
|
||||
@target('build/check-requires-timestamp', SRC, EXAMPLES_SRC_JS, SHADER_SRC,
|
||||
SPEC, SPEC_RENDERING)
|
||||
def build_check_requires_timestamp(t):
|
||||
unused_count = 0
|
||||
@@ -598,7 +597,7 @@ def build_check_requires_timestamp(t):
|
||||
t.touch()
|
||||
|
||||
|
||||
@target('build/check-whitespace-timestamp', SRC, EXAMPLES_SRC,
|
||||
@target('build/check-whitespace-timestamp', SRC, EXAMPLES_SRC_JS,
|
||||
SPEC, SPEC_RENDERING, JSDOC_SRC, precious=True)
|
||||
def build_check_whitespace_timestamp(t):
|
||||
CR_RE = re.compile(r'\r')
|
||||
@@ -691,23 +690,16 @@ def host_examples(t):
|
||||
closure_lib_path = output('node', '-e',
|
||||
'process.stdout.write(require("closure-util").getLibraryPath())')
|
||||
t.rm_rf(examples_dir)
|
||||
t.makedirs(examples_dir)
|
||||
t.cp_r('examples', examples_dir)
|
||||
for example in EXAMPLES_SRC_JS:
|
||||
split_example_file(example, examples_dir % vars(variables))
|
||||
t.cp('bin/loader_hosted_examples.js', examples_dir + '/loader.js')
|
||||
t.rm_rf(build_dir)
|
||||
t.makedirs(build_dir)
|
||||
t.rm_rf(css_dir)
|
||||
t.makedirs(css_dir)
|
||||
t.cp(EXAMPLES, examples_dir)
|
||||
for example in [path.replace('.html', '.js') for path in EXAMPLES]:
|
||||
split_example_file(example, examples_dir % vars(variables))
|
||||
for example in [path.replace('.html', '.css') for path in EXAMPLES]:
|
||||
if os.path.isfile(example):
|
||||
t.cp(example, examples_dir)
|
||||
t.cp_r('examples/data', examples_dir + '/data')
|
||||
t.cp('bin/loader_hosted_examples.js', examples_dir + '/loader.js')
|
||||
t.cp('build/ol.js', 'build/ol-debug.js', build_dir)
|
||||
t.cp('build/ol.css', css_dir)
|
||||
t.cp('examples/index.html', 'examples/example-list.js',
|
||||
'examples/example-list.xml', 'examples/Jugl.js', examples_dir)
|
||||
t.rm_rf('build/hosted/%(BRANCH)s/closure-library')
|
||||
t.cp_r(closure_lib_path, 'build/hosted/%(BRANCH)s/closure-library')
|
||||
t.rm_rf('build/hosted/%(BRANCH)s/ol')
|
||||
@@ -726,8 +718,8 @@ def host_examples(t):
|
||||
|
||||
@target('check-examples', 'host-examples', phony=True)
|
||||
def check_examples(t):
|
||||
examples = ['build/hosted/%(BRANCH)s/' + e
|
||||
for e in EXAMPLES
|
||||
examples = ['build/hosted/%(BRANCH)s/' + e.replace('examples_src', 'examples')
|
||||
for e in EXAMPLES_SRC_HTML
|
||||
if not open(e.replace('.html', '.js'), 'rU').readline().startswith('// NOCOMPILE')]
|
||||
all_examples = [e + '?mode=advanced' for e in examples]
|
||||
# Run the examples checks in a pool of threads
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" src="Jugl.js"></script>
|
||||
<script type="text/javascript" src="example-list.js"></script>
|
||||
<script type="text/javascript" src="index.js"></script>
|
||||
<script type="text/javascript">
|
||||
var template, target;
|
||||
|
||||
|
||||
@@ -46,7 +46,9 @@
|
||||
"devDependencies": {
|
||||
"clean-css": "2.2.16",
|
||||
"coveralls": "2.11.2",
|
||||
"debounce": "^1.0.0",
|
||||
"expect.js": "0.3.1",
|
||||
"gaze": "^0.5.1",
|
||||
"istanbul": "0.3.13",
|
||||
"jquery": "2.1.1",
|
||||
"jshint": "2.5.6",
|
||||
@@ -54,9 +56,9 @@
|
||||
"mocha-phantomjs": "3.5.1",
|
||||
"phantomjs": "1.9.10",
|
||||
"proj4": "2.3.3",
|
||||
"resemblejs": "1.2.0",
|
||||
"sinon": "1.10.3",
|
||||
"slimerjs-edge": "0.10.0-pre-2",
|
||||
"resemblejs": "1.2.0"
|
||||
"slimerjs-edge": "0.10.0-pre-2"
|
||||
},
|
||||
"ext": [
|
||||
"rbush"
|
||||
|
||||
@@ -81,14 +81,88 @@ function augmentExamples(files, metalsmith, done) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an inverted index of keywords from examples. Property names are
|
||||
* lowercased words. Property values are objects mapping example index to word
|
||||
* count.
|
||||
* @param {Array.<Object>} exampleInfos Array of example info objects.
|
||||
* @return {Object} Word index.
|
||||
*/
|
||||
function createWordIndex(exampleInfos) {
|
||||
var index = {};
|
||||
var keys = ['shortdesc', 'title', 'tags'];
|
||||
exampleInfos.forEach(function(info, i) {
|
||||
keys.forEach(function(key) {
|
||||
var text = info[key];
|
||||
var words = text ? text.split(/\W+/) : [];
|
||||
words.forEach(function(word) {
|
||||
if (word) {
|
||||
word = word.toLowerCase();
|
||||
var counts = index[word];
|
||||
if (counts) {
|
||||
if (index in counts) {
|
||||
counts[i] += 1;
|
||||
} else {
|
||||
counts[i] = 1;
|
||||
}
|
||||
} else {
|
||||
counts = {};
|
||||
counts[i] = 1;
|
||||
index[word] = counts;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* A plugin that generates the example index.js file. This file includes a
|
||||
* list of example metadata objects and a word index used when searching for
|
||||
* examples.
|
||||
* @param {Object} files The file lookup provided by Metalsmith. Property names
|
||||
* are file paths relative to the source directory. The file objects
|
||||
* include any existing metadata (e.g. from YAML front-matter), the file
|
||||
* contents, and stats.
|
||||
* @param {Object} metalsmith The metalsmith instance the plugin is being used
|
||||
* with.
|
||||
* @param {function(Error)} done Called when done (with any error).
|
||||
*/
|
||||
function createIndex(files, metalsmith, done) {
|
||||
setImmediate(done); // all remaining code is synchronous
|
||||
var exampleInfos = [];
|
||||
for (var filename in files) {
|
||||
var example = files[filename];
|
||||
if (markupRegEx.test(filename)) {
|
||||
exampleInfos.push({
|
||||
link: filename,
|
||||
example: filename,
|
||||
title: example.title,
|
||||
shortdesc: example.shortdesc,
|
||||
tags: example.tags
|
||||
});
|
||||
}
|
||||
}
|
||||
var info = {
|
||||
examples: exampleInfos,
|
||||
index: createWordIndex(exampleInfos)
|
||||
};
|
||||
files['index.js'] = {
|
||||
contents: new Buffer('var info = ' + JSON.stringify(info)),
|
||||
mode: '0644'
|
||||
};
|
||||
}
|
||||
|
||||
function main(callback) {
|
||||
new Metalsmith('.')
|
||||
var smith = new Metalsmith('.')
|
||||
.source(srcDir)
|
||||
.destination(destDir)
|
||||
.metadata({
|
||||
olVersion: pkg.version
|
||||
})
|
||||
.use(augmentExamples)
|
||||
.use(createIndex)
|
||||
.use(templates({
|
||||
engine: 'handlebars',
|
||||
directory: templatesDir,
|
||||
@@ -101,12 +175,15 @@ function main(callback) {
|
||||
.build(function(err) {
|
||||
callback(err);
|
||||
});
|
||||
return smith;
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main(function(err) {
|
||||
if (err) {
|
||||
process.stderr.write(err.message + '\n');
|
||||
process.stderr.write(
|
||||
'Building examples failed. See the full trace below.\n\n' +
|
||||
err.stack + '\n');
|
||||
process.exit(1);
|
||||
} else {
|
||||
process.exit(0);
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
var async = require('async');
|
||||
|
||||
var buildExt = require('./build-ext');
|
||||
var parseExamples = require('./parse-examples');
|
||||
|
||||
/**
|
||||
* Parse examples and build external modules.
|
||||
*/
|
||||
async.waterfall([
|
||||
parseExamples,
|
||||
buildExt
|
||||
], function(err) {
|
||||
buildExt(function(err) {
|
||||
if (err) {
|
||||
process.stderr.write(err + '\n');
|
||||
process.exit(1);
|
||||
} else {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
var async = require('async');
|
||||
var Parser = require('htmlparser2').Parser;
|
||||
|
||||
var buildExamples = require('./build-examples');
|
||||
|
||||
var exampleDir = path.join(__dirname, '..', 'examples');
|
||||
|
||||
|
||||
/**
|
||||
* List all .html files in the example directory (excluding index.html).
|
||||
* @param {function(Error, Array.<string>)} callback Called with any error or
|
||||
* the list of paths to examples.
|
||||
*/
|
||||
function listExamples(callback) {
|
||||
fs.readdir(exampleDir, function(err, items) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var examplePaths = items.filter(function(item) {
|
||||
return /\.html$/i.test(item) && item !== 'index.html';
|
||||
}).map(function(item) {
|
||||
return path.join(exampleDir, item);
|
||||
});
|
||||
callback(null, examplePaths);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse info from examples.
|
||||
* @param {Array.<string>} examplePaths Paths to examples.
|
||||
* @param {function(Error, Array.<Object>)} callback Called with any error or
|
||||
* the list of example info objects.
|
||||
*/
|
||||
function parseExamples(examplePaths, callback) {
|
||||
async.map(examplePaths, function(examplePath, next) {
|
||||
fs.readFile(examplePath, function(err, data) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
var name = path.basename(examplePath);
|
||||
var info = {
|
||||
link: name,
|
||||
example: name,
|
||||
title: '',
|
||||
shortdesc: '',
|
||||
tags: ''
|
||||
};
|
||||
var key;
|
||||
var openTag;
|
||||
var parser = new Parser({
|
||||
onopentag: function(tag, attrs) {
|
||||
if (attrs.id in info) {
|
||||
key = attrs.id;
|
||||
openTag = tag;
|
||||
}
|
||||
},
|
||||
ontext: function(text) {
|
||||
if (key) {
|
||||
info[key] += text.replace(/\n/g, '').trim() + ' ';
|
||||
}
|
||||
},
|
||||
onclosetag: function(tag) {
|
||||
if (tag === openTag) {
|
||||
info[key] = info[key].trim();
|
||||
key = undefined;
|
||||
openTag = undefined;
|
||||
}
|
||||
},
|
||||
onerror: function(err2) {
|
||||
var message = 'Trouble parsing ' + examplePath + '\n' + err2.message;
|
||||
next(new Error(message));
|
||||
}
|
||||
});
|
||||
parser.write(data.toString('utf8'));
|
||||
parser.end();
|
||||
next(null, info);
|
||||
});
|
||||
}, callback);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create an inverted index of keywords from examples. Property names are
|
||||
* lowercased words. Property values are objects mapping example index to word
|
||||
* count.
|
||||
* @param {Array.<Object>} exampleInfos Array of example info objects.
|
||||
* @return {Object} Word index.
|
||||
*/
|
||||
function createWordIndex(exampleInfos) {
|
||||
var index = {};
|
||||
var keys = ['shortdesc', 'title', 'tags'];
|
||||
exampleInfos.forEach(function(info, i) {
|
||||
keys.forEach(function(key) {
|
||||
var text = info[key];
|
||||
var words = text ? text.split(/\W+/) : [];
|
||||
words.forEach(function(word) {
|
||||
if (word) {
|
||||
word = word.toLowerCase();
|
||||
var counts = index[word];
|
||||
if (counts) {
|
||||
if (index in counts) {
|
||||
counts[i] += 1;
|
||||
} else {
|
||||
counts[i] = 1;
|
||||
}
|
||||
} else {
|
||||
counts = {};
|
||||
counts[i] = 1;
|
||||
index[word] = counts;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write the example-list.js file with example info and word index.
|
||||
* @param {Array.<Object>} exampleInfos Array of example info objects.
|
||||
* @param {function(Error)} callback Called with any error.
|
||||
*/
|
||||
function writeExampleList(exampleInfos, callback) {
|
||||
var info = {
|
||||
examples: exampleInfos,
|
||||
index: createWordIndex(exampleInfos)
|
||||
};
|
||||
var indexPath = path.join(exampleDir, 'example-list.js');
|
||||
var str = 'var info = ' + JSON.stringify(info);
|
||||
fs.writeFile(indexPath, str, callback);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List examples, parse them, and write example list.
|
||||
* @param {function(Error)} callback Called with any error.
|
||||
*/
|
||||
function main(callback) {
|
||||
async.waterfall([
|
||||
buildExamples,
|
||||
listExamples,
|
||||
parseExamples,
|
||||
writeExampleList
|
||||
], callback);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main(function(err) {
|
||||
if (err) {
|
||||
process.stderr.write(err.message + '\n');
|
||||
process.exit(1);
|
||||
} else {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = main;
|
||||
@@ -107,13 +107,10 @@ This is useful for projects that use the Closure Compiler to build, but want to
|
||||
Called internally to parse the library for annotations and write out a `build/info.json` file.
|
||||
|
||||
|
||||
## `parse-examples.js`
|
||||
|
||||
Called after install to generate an example index. After new examples are added, run `node tasks/parse-examples.js` to regenerate the example index.
|
||||
|
||||
## `build-examples.js`
|
||||
|
||||
Called internally by `parse-examples.js` to build the examples from templates.
|
||||
Builds examples and the example index.
|
||||
|
||||
|
||||
## `serve.js`
|
||||
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
var path = require('path');
|
||||
var url = require('url');
|
||||
|
||||
var Gaze = require('gaze').Gaze;
|
||||
var closure = require('closure-util');
|
||||
var debounce = require('debounce');
|
||||
var nomnom = require('nomnom');
|
||||
|
||||
var buildExamples = require('./build-examples');
|
||||
var log = closure.log;
|
||||
|
||||
|
||||
@@ -68,6 +71,26 @@ var createServer = exports.createServer = function(callback) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the examples and exit on any error.
|
||||
* @param {Function=} opt_callback Called when done building examples.
|
||||
*/
|
||||
function buildExamplesOrFatal(opt_callback) {
|
||||
log.info('serve', 'Building examples.');
|
||||
buildExamples(function(err) {
|
||||
if (err) {
|
||||
log.error('serve', 'Building examples failed.');
|
||||
log.error('serve', err.message);
|
||||
log.error('serve', 'Use "verbose" logging to see the full stack trace.');
|
||||
log.verbose('serve', err.stack);
|
||||
process.exit(1);
|
||||
}
|
||||
log.verbose('serve', 'Done building examples.');
|
||||
if (opt_callback) {
|
||||
opt_callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If running this module directly start the server.
|
||||
@@ -92,21 +115,30 @@ if (require.main === module) {
|
||||
/** @type {string} */
|
||||
log.level = options.loglevel;
|
||||
|
||||
log.info('serve', 'Parsing dependencies ...');
|
||||
createServer(function(err, server) {
|
||||
if (err) {
|
||||
log.error('serve', 'Parsing failed');
|
||||
log.error('serve', err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
server.listen(options.port, function() {
|
||||
log.info('serve', 'Listening on http://localhost:' +
|
||||
options.port + '/ (Ctrl+C to stop)');
|
||||
});
|
||||
server.on('error', function(err) {
|
||||
log.error('serve', 'Server failed to start: ' + err.message);
|
||||
process.exit(1);
|
||||
buildExamplesOrFatal(function() {
|
||||
log.info('serve', 'Parsing dependencies.');
|
||||
createServer(function(err, server) {
|
||||
if (err) {
|
||||
log.error('serve', 'Parsing failed');
|
||||
log.error('serve', err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
server.listen(options.port, function() {
|
||||
log.info('serve', 'Listening on http://localhost:' +
|
||||
options.port + '/ (Ctrl+C to stop)');
|
||||
});
|
||||
server.on('error', function(err) {
|
||||
log.error('serve', 'Server failed to start: ' + err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
|
||||
var gaze = new Gaze('examples_src/**/*');
|
||||
var debouncedBuild = debounce(buildExamplesOrFatal, 250);
|
||||
gaze.on('all', function(event, filepath) {
|
||||
log.verbose('serve', 'Watch event: ' + event + ' ' + filepath);
|
||||
debouncedBuild();
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user