From 451590686db7a5587879712b9d409b15697c20b2 Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Tue, 22 Nov 2016 12:03:24 +0100 Subject: [PATCH 1/2] feat(config): add file property for notes Fixes #609 --- docs/CONFIG.md | 21 ++++++++++++++++ lib/sort.js | 11 +++++++++ test/fixture/snowflake.md | 1 + test/lib/sort.js | 50 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 test/fixture/snowflake.md diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 7d9ed0903..3c335a617 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -27,3 +27,24 @@ This puts the top level API documentation for the `Map`, `LngLat`, and `LngLatBo items in the given order, and inserts a narrative item titled `Geography` after the section on maps. The `description` property of that narrative item is interpreted as Markdown. +If you would like reuse your existing markdown files or just keep the content separate from the configuration you can use the `file` property. It is a filename that will be resolved against `process.cwd()`. + +So with a `documentation.yml` file like this + +```yml +toc: + - Map + - name: Geography + file: geo.md + - LngLat + - LngLatBounds +``` + +and a file `geo.md` + +```markdown +These are Mapbox GL JS's ways of representing locations +and areas on the sphere. +``` + +it would produce the same output as the previous example. diff --git a/lib/sort.js b/lib/sort.js index ef3519143..d1a143fad 100644 --- a/lib/sort.js +++ b/lib/sort.js @@ -2,6 +2,8 @@ var parseMarkdown = require('./parse_markdown'); var chalk = require('chalk'); +var path = require('path'); +var fs = require('fs'); /** * Sort two documentation objects, given an optional order object. Returns @@ -36,6 +38,15 @@ module.exports = function sortDocs(comments, options) { var fixed = options.toc.filter(function (val) { return typeof val === 'object' && val.name; }).map(function (val) { + if (typeof val.file === 'string') { + var filename = path.join(process.cwd(), val.file); + try { + val.description = fs.readFileSync(filename).toString(); + delete val.file; + } catch (err) { + process.stderr.write(chalk.red('Failed to read file ' + filename)); + } + } if (typeof val.description === 'string') { val.description = parseMarkdown(val.description); } diff --git a/test/fixture/snowflake.md b/test/fixture/snowflake.md new file mode 100644 index 000000000..74ebe7b33 --- /dev/null +++ b/test/fixture/snowflake.md @@ -0,0 +1 @@ +# The Snowflake diff --git a/test/lib/sort.js b/test/lib/sort.js index e07adcf61..f64f088f9 100644 --- a/test/lib/sort.js +++ b/test/lib/sort.js @@ -177,3 +177,53 @@ test('sort an already-sorted stream containing a section/description', function t.deepEqual(sortTwice, [carrot, sectionMarkdown, bananas, apples]); t.end(); }); + +test('sort toc with files', function (t) { + var apples = { context: { sortKey: 'a' }, name: 'apples' }; + var carrot = { context: { sortKey: 'b' }, name: 'carrot' }; + var bananas = { context: { sortKey: 'c' }, name: 'bananas' }; + + var snowflake = { + name: 'snowflake', + file: 'test/fixture/snowflake.md' + }; + + var processedSnowflake = { + name: 'snowflake', + kind: 'note', + description: { + children: [{ + children: [{ + position: { + end: {column: 16, line: 1, offset: 15}, + indent: [], + start: {column: 3, line: 1, offset: 2} + }, + type: 'text', + value: 'The Snowflake' + }], + depth: 1, + position: { + end: {column: 16, line: 1, offset: 15}, + indent: [], + start: {column: 1, line: 1, offset: 0} + }, + type: 'heading' + }], + position: { + end: {column: 1, line: 2, offset: 16}, + start: {column: 1, line: 1, offset: 0} + }, + type: 'root' + } + }; + t.deepEqual(sort([ + apples, carrot, bananas + ], { + toc: [snowflake] + }), [ + processedSnowflake, apples, carrot, bananas + ], 'with configuration'); + + t.end(); +}); From 514d096a65d8403434941f9536d4d5d27cd6413a Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Tue, 22 Nov 2016 23:25:51 +0100 Subject: [PATCH 2/2] feat(config): resolve files against the config file location --- docs/CONFIG.md | 2 +- lib/load_config.js | 28 ++++++++++++--- lib/sort.js | 6 +++- test/config_fixture/config_file.yml | 3 ++ test/lib/load_config.js | 7 ++++ test/lib/sort.js | 53 ++++++++++++++++++++++++++++- 6 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 test/config_fixture/config_file.yml diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 3c335a617..b5976dbbb 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -27,7 +27,7 @@ This puts the top level API documentation for the `Map`, `LngLat`, and `LngLatBo items in the given order, and inserts a narrative item titled `Geography` after the section on maps. The `description` property of that narrative item is interpreted as Markdown. -If you would like reuse your existing markdown files or just keep the content separate from the configuration you can use the `file` property. It is a filename that will be resolved against `process.cwd()`. +If you would like reuse your existing markdown files or just keep the content separate from the configuration you can use the `file` property. It is a filename it will be resolved against the directory that the `documentation.yml` file resides in. So with a `documentation.yml` file like this diff --git a/lib/load_config.js b/lib/load_config.js index f57a91d4c..3e0e9781b 100644 --- a/lib/load_config.js +++ b/lib/load_config.js @@ -16,16 +16,15 @@ var yaml = require('js-yaml'), */ function loadConfig(filePath) { var ext = path.extname(filePath); - var rawFile = fs.readFileSync( - path.resolve(process.cwd(), filePath), 'utf8' - ); + var absFilePath = path.resolve(process.cwd(), filePath); + var rawFile = fs.readFileSync(absFilePath, 'utf8'); try { if (ext === '.json') { - return JSON.parse(stripComments(rawFile)); + return processToc(JSON.parse(stripComments(rawFile))); } - return yaml.safeLoad(rawFile); + return processToc(yaml.safeLoad(rawFile)); } catch (e) { e.message = 'Cannot read config file: ' + filePath + @@ -33,6 +32,25 @@ function loadConfig(filePath) { e.message; throw e; } + + function processToc(config) { + if (!config || !config.toc) { + return config; + } + + config.toc = config.toc.map(function (entry) { + if (entry && entry.file) { + entry.file = path.join( + path.dirname(absFilePath), + entry.file + ); + } + + return entry; + }); + + return config; + } } module.exports = loadConfig; diff --git a/lib/sort.js b/lib/sort.js index d1a143fad..cac44ffb4 100644 --- a/lib/sort.js +++ b/lib/sort.js @@ -39,7 +39,11 @@ module.exports = function sortDocs(comments, options) { return typeof val === 'object' && val.name; }).map(function (val) { if (typeof val.file === 'string') { - var filename = path.join(process.cwd(), val.file); + var filename = val.file; + if (!path.isAbsolute(val.file)) { + filename = path.join(process.cwd(), val.file); + } + try { val.description = fs.readFileSync(filename).toString(); delete val.file; diff --git a/test/config_fixture/config_file.yml b/test/config_fixture/config_file.yml new file mode 100644 index 000000000..cfd70b87c --- /dev/null +++ b/test/config_fixture/config_file.yml @@ -0,0 +1,3 @@ +toc: + - name: snowflake + file: ../fixture/snowflake.md diff --git a/test/lib/load_config.js b/test/lib/load_config.js index 31e5bc737..64ee5d48f 100644 --- a/test/lib/load_config.js +++ b/test/lib/load_config.js @@ -28,5 +28,12 @@ test('loadConfig', function (t) { t.deepEqual(loadConfig(path.join(__dirname, '../config_fixture/config_links.yml')), { foo: 'hello [link](https://github.com/my/link) world' }, 'config with markdown link'); + t.deepEqual(loadConfig(path.join(__dirname, '../config_fixture/config_file.yml')),{ + toc: [{ + name: 'snowflake', + file: path.join(__dirname, '../fixture/snowflake.md') + }] + }, 'config with file reference'); + t.end(); }); diff --git a/test/lib/sort.js b/test/lib/sort.js index f64f088f9..9b3f405aa 100644 --- a/test/lib/sort.js +++ b/test/lib/sort.js @@ -1,7 +1,8 @@ 'use strict'; var test = require('tap').test, - sort = require('../../lib/sort'); + sort = require('../../lib/sort'), + path = require('path'); test('sort stream alphanumeric', function (t) { var apples = { context: { sortKey: 'a' }, name: 'apples' }; @@ -227,3 +228,53 @@ test('sort toc with files', function (t) { t.end(); }); + +test('sort toc with files absolute path', function (t) { + var apples = { context: { sortKey: 'a' }, name: 'apples' }; + var carrot = { context: { sortKey: 'b' }, name: 'carrot' }; + var bananas = { context: { sortKey: 'c' }, name: 'bananas' }; + + var snowflake = { + name: 'snowflake', + file: path.join(__dirname, '../fixture/snowflake.md') + }; + + var processedSnowflake = { + name: 'snowflake', + kind: 'note', + description: { + children: [{ + children: [{ + position: { + end: {column: 16, line: 1, offset: 15}, + indent: [], + start: {column: 3, line: 1, offset: 2} + }, + type: 'text', + value: 'The Snowflake' + }], + depth: 1, + position: { + end: {column: 16, line: 1, offset: 15}, + indent: [], + start: {column: 1, line: 1, offset: 0} + }, + type: 'heading' + }], + position: { + end: {column: 1, line: 2, offset: 16}, + start: {column: 1, line: 1, offset: 0} + }, + type: 'root' + } + }; + t.deepEqual(sort([ + apples, carrot, bananas + ], { + toc: [snowflake] + }), [ + processedSnowflake, apples, carrot, bananas + ], 'with configuration'); + + t.end(); +});