readme massaging

made processors chainable
move readme massging code out of util
made github heuristic a special case
fixes #17
This commit is contained in:
Joao Moreno 2015-10-29 18:15:56 +01:00
parent 9faf610e38
commit ee533d1e69
12 changed files with 299 additions and 384 deletions

View file

@ -19,7 +19,7 @@ module.exports = function (argv: string[]): void {
.description('Packages an extension')
.option('-o, --out [path]', 'Location of the package')
.option('--baseContentUri [uri]', 'Base absolute URI that all relative URIs in the readme will get transformed as')
.action(({ out, baseContentUri }) => catchFatal(packageCommand(out, baseContentUri)));
.action(({ out, baseContentUri }) => catchFatal(packageCommand({ packagePath: out, baseContentUri })));
program
.command('publish')

View file

@ -9,8 +9,14 @@ import * as _glob from 'glob';
import * as minimatch from 'minimatch';
import * as denodeify from 'denodeify';
import * as mime from 'mime';
import * as urljoin from 'url-join';
const readFile = denodeify<string, string, string>(fs.readFile);
interface IReadFile {
(filePath: string): Promise<Buffer>;
(filePath: string, encoding?: string): Promise<string>;
}
const readFile: IReadFile = <any> denodeify(fs.readFile);
const unlink = denodeify<string, void>(fs.unlink);
const exec = denodeify<string, { cwd?: string; }, { stdout: string; stderr: string; }>(cp.exec, (err, stdout, stderr) => [err, { stdout, stderr }]);
const glob = denodeify<string, _glob.IOptions, string[]>(_glob);
@ -27,6 +33,14 @@ export interface IFile {
localPath?: string;
}
export function read(file: IFile): Promise<Buffer> {
if (file.contents) {
return Promise.resolve(file.contents);
} else {
return readFile(file.localPath);
}
}
export interface IPackageResult {
manifest: Manifest;
packagePath: string;
@ -37,17 +51,23 @@ export interface IAsset {
path: string;
}
interface IProcessor {
onFile(file: IFile): void;
export interface IPackageOptions {
cwd?: string;
packagePath?: string;
baseContentUri?: string;
}
export interface IProcessor {
onFile(file: IFile): Promise<IFile>;
assets: IAsset[];
vsix: any;
}
abstract class BaseProcessor implements IProcessor {
export abstract class BaseProcessor implements IProcessor {
constructor(protected manifest: Manifest) {}
public assets: IAsset[] = [];
public vsix: any = Object.create(null);
onFile(file: IFile): void {}
abstract onFile(file: IFile): Promise<IFile>;
}
class MainProcessor extends BaseProcessor {
@ -64,15 +84,73 @@ class MainProcessor extends BaseProcessor {
links: { repository: manifest.repository }
});
}
onFile(file: IFile): Promise<IFile> {
return Promise.resolve(file);
}
}
const README_REGEX = /^extension\/README.md$/i
class ReadmeProcessor extends BaseProcessor {
onFile(file: IFile): void {
const normalizedPath = util.normalize(file.path);
if (README_REGEX.test(normalizedPath)) {
this.assets.push({ type: 'Microsoft.VisualStudio.Services.Content.Details', path: normalizedPath });
export class ReadmeProcessor extends BaseProcessor {
private baseContentUri: string;
constructor(manifest: Manifest, options: IPackageOptions= {}) {
super(manifest);
this.baseContentUri = options.baseContentUri || this.guessBaseContentUri();
}
onFile(file: IFile): Promise<IFile> {
const path = util.normalize(file.path);
if (/^extension\/readme.md$/i.test(path)) {
this.assets.push({ type: 'Microsoft.VisualStudio.Services.Content.Details', path });
if (this.baseContentUri) {
return read(file)
.then(buffer => buffer.toString('utf8'))
.then(contents => contents.replace(/\[([^\[]+)\]\(([^\)]+)\)/g, (all, title, link) =>
all.substr(0, title.length) + all.substr(title.length).replace(link, this.prependBaseContentUri(link))
))
.then(contents => ({
path: file.path,
contents: new Buffer(contents)
}));
}
}
return Promise.resolve(file);
}
private prependBaseContentUri(link: string): string {
if (/^(?:\w+:)\/\//.test(link)) {
return link;
}
if (link[0] === '#') {
return link;
}
return urljoin(this.baseContentUri, link);
}
// GitHub heuristics
private guessBaseContentUri(): string {
let repository = null;
if (typeof this.manifest.repository === 'string') {
repository = this.manifest.repository;
} else if (this.manifest.repository && typeof this.manifest.repository['url'] === 'string') {
repository = this.manifest.repository['url'];
}
if (!repository) {
return null;
}
const regex = /github\.com\/([^/]+)\/([^/]+)(\/|$)/;
const match = regex.exec(repository);
if (match) {
return `https://raw.githubusercontent.com/${ match[1] }/${ match[2] }/master`;
}
}
}
@ -96,12 +174,13 @@ class LicenseProcessor extends BaseProcessor {
this.vsix.license = null;
}
onFile(file: IFile): void {
onFile(file: IFile): Promise<IFile> {
const normalizedPath = util.normalize(file.path);
if (this.filter(normalizedPath)) {
this.assets.push({ type: 'Microsoft.VisualStudio.Services.Content.License', path: normalizedPath });
this.vsix.license = normalizedPath;
}
return Promise.resolve(file);
}
}
@ -116,12 +195,13 @@ class IconProcessor extends BaseProcessor {
this.vsix.icon = null;
}
onFile(file: IFile): void {
onFile(file: IFile): Promise<IFile> {
const normalizedPath = util.normalize(file.path);
if (normalizedPath === this.icon) {
this.assets.push({ type: 'Microsoft.VisualStudio.Services.Icons.Default', path: normalizedPath });
this.vsix.icon = this.icon;
}
return Promise.resolve(file);
}
}
@ -162,22 +242,23 @@ export function readManifest(cwd: string): Promise<Manifest> {
});
}
export function toVsixManifest(manifest: Manifest, files: IFile[]): Promise<string> {
export function toVsixManifest(manifest: Manifest, files: IFile[], options: IPackageOptions = {}): Promise<string> {
const processors: IProcessor[] = [
new MainProcessor(manifest),
new ReadmeProcessor(manifest),
new ReadmeProcessor(manifest, options),
new LicenseProcessor(manifest),
new IconProcessor(manifest)
];
files.forEach(f => processors.forEach(p => p.onFile(f)));
const assets = _.flatten(processors.map(p => p.assets));
const vsix = (<any> _.assign)({ assets }, ...processors.map(p => p.vsix));
return readFile(vsixManifestTemplatePath, 'utf8')
.then(vsixManifestTemplateStr => _.template(vsixManifestTemplateStr))
.then(vsixManifestTemplate => vsixManifestTemplate(vsix));
return Promise.all(files.map(file => util.chain(file, processors, (file, processor) => processor.onFile(file))))
.then(files => {
const assets = _.flatten(processors.map(p => p.assets));
const vsix = (<any> _.assign)({ assets }, ...processors.map(p => p.vsix));
return readFile(vsixManifestTemplatePath, 'utf8')
.then(vsixManifestTemplateStr => _.template(vsixManifestTemplateStr))
.then(vsixManifestTemplate => vsixManifestTemplate(vsix));
});
}
export function toContentTypes(files: IFile[]): Promise<string> {
@ -218,30 +299,14 @@ function collectFiles(cwd: string, manifest: Manifest): Promise<string[]> {
});
}
function getUrlPrefix(manifest: Manifest): string {
let repository = null;
if (typeof manifest.repository === 'string') {
repository = manifest.repository;
}
if (manifest.repository && manifest.repository['url']) {
repository = manifest.repository['url'];
}
return repository ? `${ repository }/blob/master` : '';
}
export function collect(cwd: string, manifest: Manifest, baseContentUri = null): Promise<IFile[]> {
export function collect(manifest: Manifest, options: IPackageOptions = {}): Promise<IFile[]> {
const cwd = options.cwd || process.cwd();
return collectFiles(cwd, manifest).then(fileNames => {
const files:IFile[] = fileNames.map(f => ({ path: `extension/${ f }`, localPath: path.join(cwd, f) }));
const readme = files.filter(f => README_REGEX.test(util.normalize(f.path)))[0];
const prefix = baseContentUri ? baseContentUri : getUrlPrefix(manifest);
return Promise.all([toVsixManifest(manifest, files), toContentTypes(files), readme ? util.massageMarkdownLinks(readme.localPath, prefix) : ''])
return Promise.all([toVsixManifest(manifest, files, options), toContentTypes(files)])
.then(result => {
if (readme) {
readme.contents = new Buffer(result[2], 'utf8');
}
return [
{ path: 'extension.vsixmanifest', contents: new Buffer(result[0], 'utf8') },
{ path: '[Content_Types].xml', contents: new Buffer(result[1], 'utf8') },
@ -288,16 +353,18 @@ function prepublish(cwd: string, manifest: Manifest): Promise<Manifest> {
.catch(err => Promise.reject(err.message));
}
export function pack(packagePath: string = null, baseContentUri: string = null, cwd = process.cwd()): Promise<IPackageResult> {
export function pack(options: IPackageOptions = {}): Promise<IPackageResult> {
const cwd = options.cwd || process.cwd();
return readManifest(cwd)
.then(manifest => prepublish(cwd, manifest))
.then(manifest => collect(cwd, manifest, baseContentUri)
.then(files => writeVsix(files, path.resolve(packagePath || defaultPackagePath(cwd, manifest)))
.then(manifest => collect(manifest, options)
.then(files => writeVsix(files, path.resolve(options.packagePath || defaultPackagePath(cwd, manifest)))
.then(packagePath => ({ manifest, packagePath }))));
}
export function packageCommand(packagePath: string = null, baseContentUri: string = null, cwd = process.cwd()): Promise<any> {
return pack(packagePath, baseContentUri, cwd)
export function packageCommand(options: IPackageOptions = {}): Promise<any> {
return pack(options)
.then(({ packagePath }) => console.log(`Created: ${ packagePath }`));
}

View file

@ -15,7 +15,7 @@ const galleryUrl = 'https://app.market.visualstudio.com';
export function publish(cwd = process.cwd()): Promise<any> {
return tmpName()
.then(packagePath => pack(packagePath, cwd))
.then(packagePath => pack({ packagePath, cwd }))
.then(result => {
const { manifest, packagePath } = result;

View file

@ -1,31 +0,0 @@
# VSCode Extension Manager
This tool assists in publishing Visual Studio Code extensions.
[**Documentation**](https://github.com/Microsoft/vscode-extensionbuilders/blob/master/docs/tools/vscecli.md)
## Usage
First, install using npm:
```
npm install -g vsce
```
Then, `cd` to your extension's directory.
It is good practice to list the files that will be included in your extension's
package, before you actually publish:
```
$ vsce ls
hello.js
package.json
```
Publish away:
```
$ vsce publish
Publishing uuid@0.0.1...
Successfully published uuid@0.0.1!
```

View file

@ -1,121 +0,0 @@
# README
>**Important:** Once installed the checker will only update if you add the setting `"spellMD.enable": true` to your `.vscode\settings.json` file.
This README covers off:
* [Functionality](#functionality)
* [Install](#install)
* [Run and Configure](#run-and-configure)
* [Known Issues/Bugs](#known-issuesbugs)
* [Backlog](#backlog)
* [How to Debug](#how-to-debug)
# Functionality
Load up a Markdown file and get highlights and hovers for existing issues. Checking will occur as you type in the document.
![Underscores and hovers](https://github.com/Microsoft/vscode-SpellMD/raw/master/images/SpellMDDemo1.gif)
The status bar lets you quickly navigate to any issue and you can see all positions in the gutter.
![Jump to issues](https://github.com/Microsoft/vscode-SpellMD/raw/master/images/SpellMDDemo2.gif)
The `spellMD.json` config file is watched so you can add more ignores or change mappings at will.
![Add to dictionary](https://github.com/Microsoft/vscode-SpellMD/raw/master/images/SpellMDDemo3.gif)
![issue](https://github.com/Microsoft/vscode-SpellMD/raw/master/issue)
# Install
This extension is published in the VS Code Gallery. So simply hit 'F1' and type 'ext inst' from there select `SpellMD` and follow instructions.
To clone the extension and load locally...
```
git clone https://github.com/Microsoft/vscode-SpellMD.git
npm install
tsc
```
>**Note:** TypeScript 1.6 or higher is required you can check with `tsc -v` and if you need to upgrade then run `npm install -g typescript`.
Copy the extension folder into user settings.
Depending on your platform, this folder is located here:
* **Windows** `%USERPROFILE%\.vscode\extensions`
* **Mac** `$HOME/.vscode/extensions`
* **Linux** `$HOME/.vscode/extensions`
# Run and Configure
## Enable via Config setting
Add the following setting to your WorkSpace [or User] settings:
```json
"spellMD.enable": true,
```
## Open a Markdown file
Then open any Markdown file and BOOM.
## Configure
The plug-in supports and watches a config file. This should go in the `.vscode` directory and needs to be called `spellMD.json`. This file has the following sections:
* **version** incase I change the format
* **ignoreWordsList** an array of strings that represents words not to check
* **mistakeTypeToStatus** we detect many error types and this is how they map to VS Code severities
* **replaceRegExp** this is an arry of RegExps represented as strings for pre-parsing the doc e.g. removing code blocks
> **Tip:** you need to convert any `\` from the RegExp to a `\\\\` sequence for the JSON to parse.
Here is an example file...
```json
{
"version": "0.1.0",
"ignoreWordsList": [
"IntelliSense", "project.json", "nodejs", "transpiled", "ASPNET"
],
"mistakeTypeToStatus": {
"Passive voice": "Info",
"Spelling": "Error",
"Complex Expression": "Info",
"Hidden Verbs": "Info",
"Hyphen Required": "Error",
"Did you mean...": "Info",
"Repeated Word": "Error",
"Missing apostrophe": "Error",
"Redundant Expression": "Info",
"Cliches": "Warn",
"Missing Word": "Warn",
"Make I uppercase": "Error"
},
"replaceRegExp": [
"/^((`{3}\\\\s*)(\\\\w+)?(\\\\s*([\\\\w\\\\W]+?)\\\\n*)\\\\2)\\\\n*(?:[^\\\\S\\\\w\\\\s]|$)/gm",
"/\\\\]\\\\(([^\\\\)]+)\\\\)/g"
]
}
```
# Backlog
Here are some ideas - fell free to add more.
1. Let the user act on the suggestions e.g. a right-click or `Alt+S` opens command palette with suggestions, selection replaces.
2. Include the Text Tools extension w/ this i.e. WordCount, HTMLEncode etc
3. On project open check every file in the background
1. Have an `excludeFilesList` in the options
2. Suppress some types of issue completely i.e. don't report `Cliches` less noise in the list
4. Provide an action to add a word to the dictionary e.g. `Alt+A`
1. Automatically create a spellMD.json file when a user adds a word
2. When adding a word also add plurals/sentence case etc
# Debug This Code
Run this command in the directory w/ markdown files to check.
```
code --debugLanguageWorker=* --extensionDevelopmentPath="c:\src\vscode-SpellMD" .
```
Then open `VS Code` in the project directory and `Attach` to the running process.

View file

@ -1,31 +0,0 @@
# VSCode Extension Manager
This tool assists in publishing Visual Studio Code extensions.
[**Documentation**](vscode-extensionbuilders/blob/master/docs/tools/vscecli.md)
## Usage
First, install using npm:
```
npm install -g vsce
```
Then, `cd` to your extension's directory.
It is good practice to list the files that will be included in your extension's
package, before you actually publish:
```
$ vsce ls
hello.js
package.json
```
Publish away:
```
$ vsce publish
Publishing uuid@0.0.1...
Successfully published uuid@0.0.1!
```

View file

@ -1,121 +0,0 @@
# README
>**Important:** Once installed the checker will only update if you add the setting `"spellMD.enable": true` to your `.vscode\settings.json` file.
This README covers off:
* [Functionality](#functionality)
* [Install](#install)
* [Run and Configure](#run-and-configure)
* [Known Issues/Bugs](#known-issuesbugs)
* [Backlog](#backlog)
* [How to Debug](#how-to-debug)
# Functionality
Load up a Markdown file and get highlights and hovers for existing issues. Checking will occur as you type in the document.
![Underscores and hovers](/images/SpellMDDemo1.gif)
The status bar lets you quickly navigate to any issue and you can see all positions in the gutter.
![Jump to issues](/images/SpellMDDemo2.gif)
The `spellMD.json` config file is watched so you can add more ignores or change mappings at will.
![Add to dictionary](/images/SpellMDDemo3.gif)
![issue](issue)
# Install
This extension is published in the VS Code Gallery. So simply hit 'F1' and type 'ext inst' from there select `SpellMD` and follow instructions.
To clone the extension and load locally...
```
git clone https://github.com/Microsoft/vscode-SpellMD.git
npm install
tsc
```
>**Note:** TypeScript 1.6 or higher is required you can check with `tsc -v` and if you need to upgrade then run `npm install -g typescript`.
Copy the extension folder into user settings.
Depending on your platform, this folder is located here:
* **Windows** `%USERPROFILE%\.vscode\extensions`
* **Mac** `$HOME/.vscode/extensions`
* **Linux** `$HOME/.vscode/extensions`
# Run and Configure
## Enable via Config setting
Add the following setting to your WorkSpace [or User] settings:
```json
"spellMD.enable": true,
```
## Open a Markdown file
Then open any Markdown file and BOOM.
## Configure
The plug-in supports and watches a config file. This should go in the `.vscode` directory and needs to be called `spellMD.json`. This file has the following sections:
* **version** incase I change the format
* **ignoreWordsList** an array of strings that represents words not to check
* **mistakeTypeToStatus** we detect many error types and this is how they map to VS Code severities
* **replaceRegExp** this is an arry of RegExps represented as strings for pre-parsing the doc e.g. removing code blocks
> **Tip:** you need to convert any `\` from the RegExp to a `\\\\` sequence for the JSON to parse.
Here is an example file...
```json
{
"version": "0.1.0",
"ignoreWordsList": [
"IntelliSense", "project.json", "nodejs", "transpiled", "ASPNET"
],
"mistakeTypeToStatus": {
"Passive voice": "Info",
"Spelling": "Error",
"Complex Expression": "Info",
"Hidden Verbs": "Info",
"Hyphen Required": "Error",
"Did you mean...": "Info",
"Repeated Word": "Error",
"Missing apostrophe": "Error",
"Redundant Expression": "Info",
"Cliches": "Warn",
"Missing Word": "Warn",
"Make I uppercase": "Error"
},
"replaceRegExp": [
"/^((`{3}\\\\s*)(\\\\w+)?(\\\\s*([\\\\w\\\\W]+?)\\\\n*)\\\\2)\\\\n*(?:[^\\\\S\\\\w\\\\s]|$)/gm",
"/\\\\]\\\\(([^\\\\)]+)\\\\)/g"
]
}
```
# Backlog
Here are some ideas - fell free to add more.
1. Let the user act on the suggestions e.g. a right-click or `Alt+S` opens command palette with suggestions, selection replaces.
2. Include the Text Tools extension w/ this i.e. WordCount, HTMLEncode etc
3. On project open check every file in the background
1. Have an `excludeFilesList` in the options
2. Suppress some types of issue completely i.e. don't report `Cliches` less noise in the list
4. Provide an action to add a word to the dictionary e.g. `Alt+A`
1. Automatically create a spellMD.json file when a user adds a word
2. When adding a word also add plurals/sentence case etc
# Debug This Code
Run this command in the directory w/ markdown files to check.
```
code --debugLanguageWorker=* --extensionDevelopmentPath="c:\src\vscode-SpellMD" .
```
Then open `VS Code` in the project directory and `Attach` to the running process.

View file

@ -0,0 +1,41 @@
# README
>**Important:** Once installed the checker will only update if you add the setting `"spellMD.enable": true` to your `.vscode\settings.json` file.
This README covers off:
* [Functionality](#functionality)
* [Install](#install)
* [Run and Configure](#run-and-configure)
* [Known Issues/Bugs](#known-issuesbugs)
* [Backlog](#backlog)
* [How to Debug](#how-to-debug)
# Functionality
Load up a Markdown file and get highlights and hovers for existing issues. Checking will occur as you type in the document.
![Underscores and hovers](https://raw.githubusercontent.com/username/repository/master/images/SpellMDDemo1.gif)
The status bar lets you quickly navigate to any issue and you can see all positions in the gutter.
![Jump to issues](https://raw.githubusercontent.com/username/repository/master/images/SpellMDDemo2.gif)
The `spellMD.json` config file is watched so you can add more ignores or change mappings at will.
![Add to dictionary](https://raw.githubusercontent.com/username/repository/master/images/SpellMDDemo3.gif)
![issue](https://raw.githubusercontent.com/username/repository/master/issue)
# Install
This extension is published in the VS Code Gallery. So simply hit 'F1' and type 'ext inst' from there select `SpellMD` and follow instructions.
To clone the extension and load locally...
```
git clone https://github.com/Microsoft/vscode-SpellMD.git
npm install
tsc
```
>**Note:** TypeScript 1.6 or higher is required you can check with `tsc -v` and if you need to upgrade then run `npm install -g typescript`.

41
src/test/fixtures/readme/readme.md vendored Normal file
View file

@ -0,0 +1,41 @@
# README
>**Important:** Once installed the checker will only update if you add the setting `"spellMD.enable": true` to your `.vscode\settings.json` file.
This README covers off:
* [Functionality](#functionality)
* [Install](#install)
* [Run and Configure](#run-and-configure)
* [Known Issues/Bugs](#known-issuesbugs)
* [Backlog](#backlog)
* [How to Debug](#how-to-debug)
# Functionality
Load up a Markdown file and get highlights and hovers for existing issues. Checking will occur as you type in the document.
![Underscores and hovers](/images/SpellMDDemo1.gif)
The status bar lets you quickly navigate to any issue and you can see all positions in the gutter.
![Jump to issues](/images/SpellMDDemo2.gif)
The `spellMD.json` config file is watched so you can add more ignores or change mappings at will.
![Add to dictionary](/images/SpellMDDemo3.gif)
![issue](issue)
# Install
This extension is published in the VS Code Gallery. So simply hit 'F1' and type 'ext inst' from there select `SpellMD` and follow instructions.
To clone the extension and load locally...
```
git clone https://github.com/Microsoft/vscode-SpellMD.git
npm install
tsc
```
>**Note:** TypeScript 1.6 or higher is required you can check with `tsc -v` and if you need to upgrade then run `npm install -g typescript`.

View file

@ -1,4 +1,4 @@
import { readManifest, collect, toVsixManifest, toContentTypes } from '../package';
import { readManifest, collect, toVsixManifest, toContentTypes, ReadmeProcessor, read } from '../package';
import * as path from 'path';
import * as fs from 'fs';
import * as assert from 'assert';
@ -16,7 +16,7 @@ describe('collect', () => {
const cwd = fixture('uuid');
return readManifest(cwd)
.then(manifest => collect(cwd, manifest))
.then(manifest => collect(manifest, { cwd }))
.then(files => {
assert.equal(files.length, 3);
});
@ -34,7 +34,7 @@ describe('collect', () => {
}
return readManifest(cwd)
.then(manifest => collect(cwd, manifest))
.then(manifest => collect(manifest, { cwd }))
.then(files => {
assert.equal(files.length, 3);
});
@ -44,7 +44,7 @@ describe('collect', () => {
const cwd = fixture('devDependencies');
return readManifest(cwd)
.then(manifest => collect(cwd, manifest))
.then(manifest => collect(manifest, { cwd }))
.then(files => {
assert.equal(files.length, 4);
assert.ok(files.some(f => /real\/dependency\.js/.test(f.path)));
@ -312,22 +312,90 @@ describe('toContentTypes', () => {
});
});
describe('readmeMassaging', () => {
it('should prepend links', () => {
return util.massageMarkdownLinks(path.join(process.cwd(), '/src/test/assets/relativeLinks1.md'), 'https://github.com/Microsoft/')
.then(result => readFile(path.join(process.cwd(), '/src/test/assets/absoluteLinks1.md'), 'utf-8')
.then(expected => {
assert.equal(result, expected);
})
);
describe('ReadmeProcessor', () => {
it('should be no-op when no baseContentUri is provided', () => {
const manifest = {
name: 'test',
publisher: 'mocha',
version: '0.0.1',
description: 'test extension',
engines: Object.create(null)
};
const root = fixture('readme');
const processor = new ReadmeProcessor(manifest);
const readme = {
path: 'extension/readme.md',
localPath: path.join(root, 'readme.md')
};
return processor.onFile(readme)
.then(file => read(file))
.then(actualBuffer => {
const actual = actualBuffer.toString('utf8');
return readFile(path.join(root, 'readme.md'), 'utf8')
.then(expected => {
assert.equal(actual, expected);
})
});
});
it('should prepend links 2', () => {
return util.massageMarkdownLinks(path.join(process.cwd(), '/src/test/assets/relativeLinks2.md'), 'https://github.com/Microsoft/vscode-SpellMD/raw/master/')
.then(result => readFile(path.join(process.cwd(), '/src/test/assets/absoluteLinks2.md'), 'utf-8')
.then(expected => {
assert.equal(result, expected);
})
);
it('should take baseContentUri', () => {
const manifest = {
name: 'test',
publisher: 'mocha',
version: '0.0.1',
description: 'test extension',
engines: Object.create(null)
};
const root = fixture('readme');
const processor = new ReadmeProcessor(manifest, { baseContentUri: 'https://raw.githubusercontent.com/username/repository/master' });
const readme = {
path: 'extension/readme.md',
localPath: path.join(root, 'readme.md')
};
return processor.onFile(readme)
.then(file => read(file))
.then(actualBuffer => {
const actual = actualBuffer.toString('utf8');
return readFile(path.join(root, 'readme.expected.md'), 'utf8')
.then(expected => {
assert.equal(actual, expected);
})
});
});
it('should infer baseContentUri if its a github repo', () => {
const manifest = {
name: 'test',
publisher: 'mocha',
version: '0.0.1',
description: 'test extension',
engines: Object.create(null),
repository: 'https://github.com/username/repository'
};
const root = fixture('readme');
const processor = new ReadmeProcessor(manifest);
const readme = {
path: 'extension/readme.md',
localPath: path.join(root, 'readme.md')
};
return processor.onFile(readme)
.then(file => read(file))
.then(actualBuffer => {
const actual = actualBuffer.toString('utf8');
return readFile(path.join(root, 'readme.expected.md'), 'utf8')
.then(expected => {
assert.equal(actual, expected);
})
});
});
});

View file

@ -5,7 +5,6 @@ import * as path from 'path';
import { WebApi, getBasicHandler } from 'vso-node-api/WebApi';
import { IGalleryApi, IQGalleryApi } from 'vso-node-api/GalleryApi';
import * as denodeify from 'denodeify';
import urljoin = require('url-join');
const readFile = denodeify<string, string, string>(fs.readFile);
@ -48,13 +47,14 @@ export function normalize(path: string): string {
return path.replace(/\\/g, '/');
}
export function massageMarkdownLinks(pathToMarkdown: string, prefix: string): Promise<string> {
return readFile(pathToMarkdown, 'utf8').then(markdown => markdown.replace(/\[([^\[]+)\]\(([^\)]+)\)/g, (titleAndLink, title, link) =>
titleAndLink.substr(0, title.length) + titleAndLink.substr(title.length).replace(link, prependRelativeLink(link, prefix))
));
function chain2<A,B>(a: A, b: B[], fn: (a: A, b: B)=>Promise<A>, index = 0): Promise<A> {
if (index >= b.length) {
return Promise.resolve(a);
}
return fn(a, b[index]).then(a => chain2(a, b, fn, index + 1));
}
function prependRelativeLink(link: string, prefix: string): string {
// Prepend only relative links, also ignore links to the sections in markdown (they contain #).
return /^(?:\w+:)\/\//.test(link) || link.indexOf('#') !== -1 ? link : urljoin(prefix, link);
}
export function chain<T,P>(initial: T, processors: P[], process: (a: T, b: P)=>Promise<T>): Promise<T> {
return chain2(initial, processors, process);
}

View file

@ -1,5 +1,7 @@
declare module 'url-join' {
function join(...args: string[]): string;
module join {}
export = join;
}