Merge branch 'master' into pr/396
This commit is contained in:
commit
6adca5365f
22 changed files with 748 additions and 480 deletions
24
.vscode/tasks.json
vendored
24
.vscode/tasks.json
vendored
|
@ -1,25 +1,23 @@
|
|||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "compile",
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": [
|
||||
"$tsc-watch"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"command": "npm",
|
||||
"presentation": {
|
||||
"reveal": "silent"
|
||||
},
|
||||
"args": [
|
||||
"run",
|
||||
"compile",
|
||||
"--loglevel",
|
||||
"silent"
|
||||
],
|
||||
"problemMatcher": [],
|
||||
"isBackground": true
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -8,6 +8,12 @@
|
|||
|
||||
- [Node.js](https://nodejs.org/en/) at least `8.x.x`
|
||||
|
||||
## Usage
|
||||
|
||||
`vsce` is meant to be mainly used as a command line tool. It can also be used a library since it exposes a small [API](https://github.com/microsoft/vscode-vsce/blob/master/src/api.ts).
|
||||
|
||||
> **Warning:** When using vsce as a library be sure to sanitize any user input used in API calls, as a security measurement.
|
||||
|
||||
## Development
|
||||
|
||||
First clone this repository, then:
|
||||
|
|
21
package.json
21
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "vsce",
|
||||
"version": "1.68.0",
|
||||
"version": "1.76.1",
|
||||
"description": "VSCode Extension Manager",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -40,10 +40,10 @@
|
|||
"cheerio": "^1.0.0-rc.1",
|
||||
"commander": "^2.8.1",
|
||||
"denodeify": "^1.2.1",
|
||||
"didyoumean": "^1.2.1",
|
||||
"glob": "^7.0.6",
|
||||
"lodash": "^4.17.10",
|
||||
"markdown-it": "^8.3.1",
|
||||
"leven": "^3.1.0",
|
||||
"lodash": "^4.17.15",
|
||||
"markdown-it": "^10.0.0",
|
||||
"mime": "^1.3.4",
|
||||
"minimatch": "^3.0.3",
|
||||
"osenv": "^0.1.3",
|
||||
|
@ -59,22 +59,27 @@
|
|||
"devDependencies": {
|
||||
"@types/cheerio": "^0.22.1",
|
||||
"@types/denodeify": "^1.2.31",
|
||||
"@types/didyoumean": "^1.2.0",
|
||||
"@types/glob": "^7.1.1",
|
||||
"@types/lodash": "^4.14.123",
|
||||
"@types/markdown-it": "0.0.2",
|
||||
"@types/mime": "^1",
|
||||
"@types/minimatch": "^3.0.3",
|
||||
"@types/mocha": "^5.2.6",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/node": "^8",
|
||||
"@types/read": "^0.0.28",
|
||||
"@types/semver": "^6.0.0",
|
||||
"@types/tmp": "^0.1.0",
|
||||
"@types/xml2js": "^0.4.4",
|
||||
"concurrently": "^4.1.0",
|
||||
"mocha": "^5.2.0",
|
||||
"concurrently": "^5.1.0",
|
||||
"mocha": "^7.1.1",
|
||||
"source-map-support": "^0.4.2",
|
||||
"typescript": "^3.4.3",
|
||||
"xml2js": "^0.4.12"
|
||||
},
|
||||
"mocha": {
|
||||
"require": [
|
||||
"source-map-support/register"
|
||||
],
|
||||
"spec": "out/test"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,13 @@ export interface IListFilesOptions {
|
|||
* no dependencies will be included.
|
||||
*/
|
||||
packagedDependencies?: string[];
|
||||
|
||||
/**
|
||||
* The location of an alternative .vscodeignore file to be used.
|
||||
* The `.vscodeignore` file located at the root of the project will be taken
|
||||
* instead, if none is specified.
|
||||
*/
|
||||
ignoreFile?: string;
|
||||
}
|
||||
|
||||
export interface IPublishVSIXOptions {
|
||||
|
@ -135,7 +142,7 @@ export function publish(options: IPublishOptions = {}): Promise<any> {
|
|||
* Lists the files included in the extension's package.
|
||||
*/
|
||||
export function listFiles(options: IListFilesOptions = {}): Promise<string[]> {
|
||||
return _listFiles(options.cwd, options.packageManager === PackageManager.Yarn, options.packagedDependencies);
|
||||
return _listFiles(options.cwd, options.packageManager === PackageManager.Yarn, options.packagedDependencies, options.ignoreFile);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
24
src/main.ts
24
src/main.ts
|
@ -1,5 +1,5 @@
|
|||
import * as program from 'commander';
|
||||
import * as didYouMean from 'didyoumean';
|
||||
import * as leven from 'leven';
|
||||
|
||||
import { packageCommand, ls } from './package';
|
||||
import { publish, unpublish } from './publish';
|
||||
|
@ -7,12 +7,12 @@ import { show } from './show';
|
|||
import { search } from './search';
|
||||
import { listPublishers, createPublisher, deletePublisher, loginPublisher, logoutPublisher } from './store';
|
||||
import { getLatestVersion } from './npm';
|
||||
import { CancellationToken, isCancelledError, log } from './util';
|
||||
import { CancellationToken, log } from './util';
|
||||
import * as semver from 'semver';
|
||||
import { isatty } from 'tty';
|
||||
const pkg = require('../package.json');
|
||||
|
||||
function fatal<T>(message: any, ...args: any[]): void {
|
||||
function fatal(message: any, ...args: any[]): void {
|
||||
if (message instanceof Error) {
|
||||
message = message.message;
|
||||
|
||||
|
@ -63,7 +63,8 @@ module.exports = function (argv: string[]): void {
|
|||
.description('Lists all the files that will be published')
|
||||
.option('--yarn', 'Use yarn instead of npm')
|
||||
.option('--packagedDependencies <path>', 'Select packages that should be published only (includes dependencies)', (val, all) => all ? all.concat(val) : [val], undefined)
|
||||
.action(({ yarn, packagedDependencies }) => main(ls(undefined, yarn, packagedDependencies)));
|
||||
.option('--ignoreFile [path]', 'Indicate alternative .vscodeignore')
|
||||
.action(({ yarn, packagedDependencies, ignoreFile }) => main(ls(undefined, yarn, packagedDependencies, ignoreFile)));
|
||||
|
||||
program
|
||||
.command('package')
|
||||
|
@ -72,25 +73,29 @@ module.exports = function (argv: string[]): void {
|
|||
.option('--baseContentUrl [url]', 'Prepend all relative links in README.md with this url.')
|
||||
.option('--baseImagesUrl [url]', 'Prepend all relative image links in README.md with this url.')
|
||||
.option('--yarn', 'Use yarn instead of npm')
|
||||
.action(({ out, baseContentUrl, baseImagesUrl, yarn }) => main(packageCommand({ packagePath: out, baseContentUrl, baseImagesUrl, useYarn: yarn })));
|
||||
.option('--ignoreFile [path]', 'Indicate alternative .vscodeignore')
|
||||
.option('--noGitHubIssueLinking', 'Prevent automatic expansion of GitHub-style issue syntax into links')
|
||||
.action(({ out, baseContentUrl, baseImagesUrl, yarn, ignoreFile, noGitHubIssueLinking }) => main(packageCommand({ packagePath: out, baseContentUrl, baseImagesUrl, useYarn: yarn, ignoreFile, expandGitHubIssueLinks: noGitHubIssueLinking })));
|
||||
|
||||
program
|
||||
.command('publish [<version>]')
|
||||
.description('Publishes an extension')
|
||||
.option('-p, --pat <token>', 'Personal Access Token')
|
||||
.option('-p, --pat <token>', 'Personal Access Token', process.env['VSCE_PAT'])
|
||||
.option('-m, --message <commit message>', 'Commit message used when calling `npm version`.')
|
||||
.option('--packagePath [path]', 'Publish the VSIX package located at the specified path.')
|
||||
.option('--baseContentUrl [url]', 'Prepend all relative links in README.md with this url.')
|
||||
.option('--baseImagesUrl [url]', 'Prepend all relative image links in README.md with this url.')
|
||||
.option('--yarn', 'Use yarn instead of npm while packing extension files')
|
||||
.option('--noVerify')
|
||||
.action((version, { pat, message, packagePath, baseContentUrl, baseImagesUrl, yarn, noVerify }) => main(publish({ pat, commitMessage: message, version, packagePath, baseContentUrl, baseImagesUrl, useYarn: yarn, noVerify })));
|
||||
.option('--ignoreFile [path]', 'Indicate alternative .vscodeignore')
|
||||
.action((version, { pat, message, packagePath, baseContentUrl, baseImagesUrl, yarn, noVerify, ignoreFile }) => main(publish({ pat, commitMessage: message, version, packagePath, baseContentUrl, baseImagesUrl, useYarn: yarn, noVerify, ignoreFile })));
|
||||
|
||||
program
|
||||
.command('unpublish [<extensionid>]')
|
||||
.description('Unpublishes an extension. Example extension id: microsoft.csharp.')
|
||||
.option('-p, --pat <token>', 'Personal Access Token')
|
||||
.action((id, { pat }) => main(unpublish({ id, pat })));
|
||||
.option('-f, --force', 'Forces Unpublished Extension')
|
||||
.action((id, { pat, force }) => main(unpublish({ id, pat, force })));
|
||||
|
||||
program
|
||||
.command('ls-publishers')
|
||||
|
@ -133,7 +138,8 @@ module.exports = function (argv: string[]): void {
|
|||
.command('*', '', { noHelp: true })
|
||||
.action((cmd: string) => {
|
||||
program.help(help => {
|
||||
const suggestion = didYouMean(cmd, program.commands.map(c => c._name));
|
||||
const availableCommands = program.commands.map(c => c._name);
|
||||
const suggestion = availableCommands.find(c => leven(c, cmd) < c.length * 0.4);
|
||||
|
||||
help = `${help}
|
||||
Unknown command '${cmd}'`;
|
||||
|
|
|
@ -42,6 +42,7 @@ export interface Manifest {
|
|||
_testing?: string;
|
||||
enableProposedApi?: boolean;
|
||||
qna?: 'marketplace' | string | false;
|
||||
extensionKind?: string[];
|
||||
|
||||
// optional (npm)
|
||||
author?: string | Person;
|
||||
|
|
|
@ -3,7 +3,7 @@ import * as fs from 'fs';
|
|||
import * as cp from 'child_process';
|
||||
import * as parseSemver from 'parse-semver';
|
||||
import * as _ from 'lodash';
|
||||
import { CancellationToken, log } from './util';
|
||||
import { CancellationToken } from './util';
|
||||
|
||||
interface IOptions {
|
||||
cwd?: string;
|
||||
|
@ -54,7 +54,7 @@ function checkNPM(cancellationToken?: CancellationToken): Promise<void> {
|
|||
|
||||
function getNpmDependencies(cwd: string): Promise<string[]> {
|
||||
return checkNPM()
|
||||
.then(() => exec('npm list --production --parseable --depth=99999', { cwd, maxBuffer: 5000 * 1024 }))
|
||||
.then(() => exec('npm list --production --parseable --depth=99999 --loglevel=error', { cwd, maxBuffer: 5000 * 1024 }))
|
||||
.then(({ stdout }) => stdout
|
||||
.split(/[\r\n]/)
|
||||
.filter(dir => path.isAbsolute(dir)));
|
||||
|
|
153
src/package.ts
153
src/package.ts
|
@ -17,15 +17,9 @@ import * as urljoin from 'url-join';
|
|||
import { validatePublisher, validateExtensionName, validateVersion, validateEngineCompatibility, validateVSCodeTypesCompatibility } from './validation';
|
||||
import { getDependencies } from './npm';
|
||||
|
||||
interface IReadFile {
|
||||
(filePath: string): Promise<Buffer>;
|
||||
(filePath: string, encoding?: string): Promise<string>;
|
||||
}
|
||||
|
||||
const readFile = denodeify<string, string, string>(fs.readFile);
|
||||
const unlink = denodeify<string, void>(fs.unlink as any);
|
||||
const stat = denodeify(fs.stat);
|
||||
const exec = denodeify<string, { cwd?: string; env?: any; maxBuffer?: number; }, { stdout: string; stderr: string; }>(cp.exec as any, (err, stdout, stderr) => [err, { stdout, stderr }]);
|
||||
const glob = denodeify<string, _glob.IOptions, string[]>(_glob);
|
||||
|
||||
const resourcesPath = path.join(path.dirname(__dirname), 'resources');
|
||||
|
@ -69,6 +63,8 @@ export interface IPackageOptions {
|
|||
baseImagesUrl?: string;
|
||||
useYarn?: boolean;
|
||||
dependencyEntryPoints?: string[];
|
||||
ignoreFile?: string;
|
||||
expandGitHubIssueLinks?: boolean;
|
||||
}
|
||||
|
||||
export interface IProcessor {
|
||||
|
@ -244,6 +240,10 @@ class ManifestProcessor extends BaseProcessor {
|
|||
}
|
||||
|
||||
async onEnd(): Promise<void> {
|
||||
if (typeof this.manifest.extensionKind === 'string') {
|
||||
util.log.warn(`The 'extensionKind' property should be of type 'string[]'. Learn more at: https://aka.ms/vscode/api/incorrect-execution-location`);
|
||||
}
|
||||
|
||||
if (this.manifest.publisher === 'vscode-samples') {
|
||||
throw new Error('It\'s not allowed to use the \'vscode-samples\' publisher. Learn more at: https://code.visualstudio.com/api/working-with-extensions/publishing-extension.');
|
||||
}
|
||||
|
@ -360,6 +360,7 @@ export class MarkdownProcessor extends BaseProcessor {
|
|||
private baseImagesUrl: string;
|
||||
private isGitHub: boolean;
|
||||
private repositoryUrl: string;
|
||||
private expandGitHubIssueLinks: boolean;
|
||||
|
||||
constructor(manifest: Manifest, private name: string, private regexp: RegExp, private assetType: string, options: IPackageOptions = {}) {
|
||||
super(manifest);
|
||||
|
@ -370,6 +371,7 @@ export class MarkdownProcessor extends BaseProcessor {
|
|||
this.baseImagesUrl = options.baseImagesUrl || options.baseContentUrl || (guess && guess.images);
|
||||
this.repositoryUrl = (guess && guess.repository);
|
||||
this.isGitHub = isGitHubRepository(this.repositoryUrl);
|
||||
this.expandGitHubIssueLinks = typeof options.expandGitHubIssueLinks === 'boolean' ? options.expandGitHubIssueLinks : true;
|
||||
}
|
||||
|
||||
async onFile(file: IFile): Promise<IFile> {
|
||||
|
@ -388,7 +390,7 @@ export class MarkdownProcessor extends BaseProcessor {
|
|||
}
|
||||
|
||||
const markdownPathRegex = /(!?)\[([^\]\[]*|!\[[^\]\[]*]\([^\)]+\))\]\(([^\)]+)\)/g;
|
||||
const urlReplace = (all, isImage, title, link) => {
|
||||
const urlReplace = (_, isImage, title, link) => {
|
||||
const isLinkRelative = !/^\w+:\/\//.test(link) && link[0] !== '#';
|
||||
|
||||
if (!this.baseContentUrl && !this.baseImagesUrl) {
|
||||
|
@ -408,20 +410,37 @@ export class MarkdownProcessor extends BaseProcessor {
|
|||
|
||||
return `${isImage}[${title}](${urljoin(prefix, link)})`;
|
||||
};
|
||||
|
||||
// Replace Markdown links with urls
|
||||
contents = contents.replace(markdownPathRegex, urlReplace);
|
||||
|
||||
const markdownIssueRegex = /(\s|\n)([\w\d_-]+\/[\w\d_-]+)?#(\d+)\b/g
|
||||
const issueReplace = (all: string, prefix: string, ownerAndRepositoryName: string, issueNumber: string): string => {
|
||||
let result = all;
|
||||
let owner: string;
|
||||
let repositoryName: string;
|
||||
// Replace <img> links with urls
|
||||
contents = contents.replace(/<img.+?src=["']([/.\w\s-]+)['"].*?>/g, (all, link) => {
|
||||
const isLinkRelative = !/^\w+:\/\//.test(link) && link[0] !== '#';
|
||||
|
||||
if (ownerAndRepositoryName) {
|
||||
[owner, repositoryName] = ownerAndRepositoryName.split('/', 2);
|
||||
if (!this.baseImagesUrl && isLinkRelative) {
|
||||
throw new Error(`Couldn't detect the repository where this extension is published. The image will be broken in ${this.name}. Please provide the repository URL in package.json or use the --baseContentUrl and --baseImagesUrl options.`);
|
||||
}
|
||||
const prefix = this.baseImagesUrl;
|
||||
|
||||
if (!prefix || !isLinkRelative) {
|
||||
return all;
|
||||
}
|
||||
|
||||
if (this.isGitHub) {
|
||||
return all.replace(link, urljoin(prefix, link));
|
||||
});
|
||||
|
||||
if (this.isGitHub && this.expandGitHubIssueLinks) {
|
||||
const markdownIssueRegex = /(\s|\n)([\w\d_-]+\/[\w\d_-]+)?#(\d+)\b/g
|
||||
const issueReplace = (all: string, prefix: string, ownerAndRepositoryName: string, issueNumber: string): string => {
|
||||
let result = all;
|
||||
let owner: string;
|
||||
let repositoryName: string;
|
||||
|
||||
if (ownerAndRepositoryName) {
|
||||
[owner, repositoryName] = ownerAndRepositoryName.split('/', 2);
|
||||
}
|
||||
|
||||
if (owner && repositoryName && issueNumber) {
|
||||
// Issue in external repository
|
||||
const issueUrl = urljoin('https://github.com', owner, repositoryName, 'issues', issueNumber);
|
||||
|
@ -431,12 +450,12 @@ export class MarkdownProcessor extends BaseProcessor {
|
|||
// Issue in own repository
|
||||
result = prefix + `[#${issueNumber}](${urljoin(this.repositoryUrl, 'issues', issueNumber)})`;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
// Replace Markdown issue references with urls
|
||||
contents = contents.replace(markdownIssueRegex, issueReplace);
|
||||
}
|
||||
// Replace Markdown issue references with urls
|
||||
contents = contents.replace(markdownIssueRegex, issueReplace);
|
||||
|
||||
const html = markdownit({ html: true }).render(contents);
|
||||
const $ = cheerio.load(html);
|
||||
|
@ -458,13 +477,13 @@ export class MarkdownProcessor extends BaseProcessor {
|
|||
}
|
||||
});
|
||||
|
||||
$('svg').each((_, svg) => {
|
||||
$('svg').each(() => {
|
||||
throw new Error(`SVG tags are not allowed in ${this.name}.`);
|
||||
});
|
||||
|
||||
return {
|
||||
path: file.path,
|
||||
contents: new Buffer(contents)
|
||||
contents: Buffer.from(contents, 'utf8')
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -747,7 +766,7 @@ export function readManifest(cwd = process.cwd(), nls = true): Promise<Manifest>
|
|||
|
||||
}
|
||||
|
||||
export function toVsixManifest(assets: IAsset[], vsix: any, options: IPackageOptions = {}): Promise<string> {
|
||||
export function toVsixManifest(vsix: any): Promise<string> {
|
||||
return readFile(vsixManifestTemplatePath, 'utf8')
|
||||
.then(vsixManifestTemplateStr => _.template(vsixManifestTemplateStr))
|
||||
.then(vsixManifestTemplate => vsixManifestTemplate(vsix));
|
||||
|
@ -814,12 +833,12 @@ function collectAllFiles(cwd: string, useYarn = false, dependencyEntryPoints?: s
|
|||
});
|
||||
}
|
||||
|
||||
function collectFiles(cwd: string, useYarn = false, dependencyEntryPoints?: string[]): Promise<string[]> {
|
||||
function collectFiles(cwd: string, useYarn = false, dependencyEntryPoints?: string[], ignoreFile?: string): Promise<string[]> {
|
||||
return collectAllFiles(cwd, useYarn, dependencyEntryPoints).then(files => {
|
||||
files = files.filter(f => !/\r$/m.test(f));
|
||||
|
||||
return readFile(path.join(cwd, '.vscodeignore'), 'utf8')
|
||||
.catch<string>(err => err.code !== 'ENOENT' ? Promise.reject(err) : Promise.resolve(''))
|
||||
return readFile(ignoreFile ? ignoreFile : path.join(cwd, '.vscodeignore'), 'utf8')
|
||||
.catch<string>(err => err.code !== 'ENOENT' ? Promise.reject(err) : ignoreFile ? Promise.reject(err) : Promise.resolve(''))
|
||||
|
||||
// Parse raw ignore by splitting output into lines and filtering out empty lines and comments
|
||||
.then(rawIgnore => rawIgnore.split(/[\n\r]/).map(s => s.trim()).filter(s => !!s).filter(i => !/^\s*#/.test(i)))
|
||||
|
@ -839,7 +858,7 @@ function collectFiles(cwd: string, useYarn = false, dependencyEntryPoints?: stri
|
|||
});
|
||||
}
|
||||
|
||||
export function processFiles(processors: IProcessor[], files: IFile[], options: IPackageOptions = {}): Promise<IFile[]> {
|
||||
export function processFiles(processors: IProcessor[], files: IFile[]): Promise<IFile[]> {
|
||||
const processedFiles = files.map(file => util.chain(file, processors, (file, processor) => processor.onFile(file)));
|
||||
|
||||
return Promise.all(processedFiles).then(files => {
|
||||
|
@ -847,10 +866,10 @@ export function processFiles(processors: IProcessor[], files: IFile[], options:
|
|||
const assets = _.flatten(processors.map(p => p.assets));
|
||||
const vsix = processors.reduce((r, p) => ({ ...r, ...p.vsix }), { assets });
|
||||
|
||||
return Promise.all([toVsixManifest(assets, vsix, options), toContentTypes(files)]).then(result => {
|
||||
return Promise.all([toVsixManifest(vsix), toContentTypes(files)]).then(result => {
|
||||
return [
|
||||
{ path: 'extension.vsixmanifest', contents: new Buffer(result[0], 'utf8') },
|
||||
{ path: '[Content_Types].xml', contents: new Buffer(result[1], 'utf8') },
|
||||
{ path: 'extension.vsixmanifest', contents: Buffer.from(result[0], 'utf8') },
|
||||
{ path: '[Content_Types].xml', contents: Buffer.from(result[1], 'utf8') },
|
||||
...files
|
||||
];
|
||||
});
|
||||
|
@ -875,21 +894,22 @@ export function collect(manifest: Manifest, options: IPackageOptions = {}): Prom
|
|||
const cwd = options.cwd || process.cwd();
|
||||
const useYarn = options.useYarn || false;
|
||||
const packagedDependencies = options.dependencyEntryPoints || undefined;
|
||||
const ignoreFile = options.ignoreFile || undefined;
|
||||
const processors = createDefaultProcessors(manifest, options);
|
||||
|
||||
return collectFiles(cwd, useYarn, packagedDependencies).then(fileNames => {
|
||||
return collectFiles(cwd, useYarn, packagedDependencies, ignoreFile).then(fileNames => {
|
||||
const files = fileNames.map(f => ({ path: `extension/${f}`, localPath: path.join(cwd, f) }));
|
||||
|
||||
return processFiles(processors, files, options);
|
||||
return processFiles(processors, files);
|
||||
});
|
||||
}
|
||||
|
||||
function writeVsix(files: IFile[], packagePath: string): Promise<string> {
|
||||
function writeVsix(files: IFile[], packagePath: string): Promise<void> {
|
||||
return unlink(packagePath)
|
||||
.catch(err => err.code !== 'ENOENT' ? Promise.reject(err) : Promise.resolve(null))
|
||||
.then(() => new Promise<string>((c, e) => {
|
||||
.then(() => new Promise((c, e) => {
|
||||
const zip = new yazl.ZipFile();
|
||||
files.forEach(f => f.contents ? zip.addBuffer(typeof f.contents === 'string' ? new Buffer(f.contents, 'utf8') : f.contents, f.path) : zip.addFile(f.localPath, f.path));
|
||||
files.forEach(f => f.contents ? zip.addBuffer(typeof f.contents === 'string' ? Buffer.from(f.contents, 'utf8') : f.contents, f.path) : zip.addFile(f.localPath, f.path));
|
||||
zip.end();
|
||||
|
||||
const zipStream = fs.createWriteStream(packagePath);
|
||||
|
@ -897,41 +917,62 @@ function writeVsix(files: IFile[], packagePath: string): Promise<string> {
|
|||
|
||||
zip.outputStream.once('error', e);
|
||||
zipStream.once('error', e);
|
||||
zipStream.once('finish', () => c(packagePath));
|
||||
zipStream.once('finish', () => c());
|
||||
}));
|
||||
}
|
||||
|
||||
function defaultPackagePath(cwd: string, manifest: Manifest): string {
|
||||
return path.join(cwd, `${manifest.name}-${manifest.version}.vsix`);
|
||||
function getDefaultPackageName(manifest: Manifest): string {
|
||||
return `${manifest.name}-${manifest.version}.vsix`;
|
||||
}
|
||||
|
||||
function prepublish(cwd: string, manifest: Manifest, useYarn: boolean = false): Promise<Manifest> {
|
||||
async function prepublish(cwd: string, manifest: Manifest, useYarn: boolean = false): Promise<void> {
|
||||
if (!manifest.scripts || !manifest.scripts['vscode:prepublish']) {
|
||||
return Promise.resolve(manifest);
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn(`Executing prepublish script '${useYarn ? 'yarn' : 'npm'} run vscode:prepublish'...`);
|
||||
console.log(`Executing prepublish script '${useYarn ? 'yarn' : 'npm'} run vscode:prepublish'...`);
|
||||
|
||||
return exec(`${useYarn ? 'yarn' : 'npm'} run vscode:prepublish`, { cwd, maxBuffer: 5000 * 1024 })
|
||||
.then(({ stdout, stderr }) => {
|
||||
process.stdout.write(stdout);
|
||||
process.stderr.write(stderr);
|
||||
return Promise.resolve(manifest);
|
||||
})
|
||||
.catch(err => Promise.reject(err.message));
|
||||
await new Promise((c, e) => {
|
||||
const tool = useYarn ? 'yarn' : 'npm';
|
||||
const child = cp.spawn(tool, ['run', 'vscode:prepublish'], { cwd, shell: true, stdio: 'inherit' });
|
||||
child.on('exit', code => code === 0 ? c() : e(`${tool} failed with exit code ${code}`));
|
||||
child.on('error', e);
|
||||
});
|
||||
}
|
||||
|
||||
async function getPackagePath(cwd: string, manifest: Manifest, options: IPackageOptions = {}): Promise<string> {
|
||||
if (!options.packagePath) {
|
||||
return path.join(cwd, getDefaultPackageName(manifest));
|
||||
}
|
||||
|
||||
try {
|
||||
const _stat = await stat(options.packagePath);
|
||||
|
||||
if (_stat.isDirectory()) {
|
||||
return path.join(options.packagePath, getDefaultPackageName(manifest));
|
||||
} else {
|
||||
return options.packagePath;
|
||||
}
|
||||
} catch {
|
||||
return options.packagePath;
|
||||
}
|
||||
}
|
||||
|
||||
export async function pack(options: IPackageOptions = {}): Promise<IPackageResult> {
|
||||
const cwd = options.cwd || process.cwd();
|
||||
|
||||
let manifest = await readManifest(cwd);
|
||||
manifest = await prepublish(cwd, manifest, options.useYarn);
|
||||
const manifest = await readManifest(cwd);
|
||||
await prepublish(cwd, manifest, options.useYarn);
|
||||
|
||||
const files = await collect(manifest, options);
|
||||
if (files.length > 100) {
|
||||
console.log(`This extension consists of ${files.length} separate files. For performance reasons, you should bundle your extension: https://aka.ms/vscode-bundle-extension . You should also exclude unnecessary files by adding them to your .vscodeignore: https://aka.ms/vscode-vscodeignore`);
|
||||
const jsFiles = files.filter(f => /\.js$/i.test(f.path));
|
||||
|
||||
if (files.length > 5000 || jsFiles.length > 100) {
|
||||
console.log(`This extension consists of ${files.length} files, out of which ${jsFiles.length} are JavaScript files. For performance reasons, you should bundle your extension: https://aka.ms/vscode-bundle-extension . You should also exclude unnecessary files by adding them to your .vscodeignore: https://aka.ms/vscode-vscodeignore`);
|
||||
}
|
||||
const packagePath = await writeVsix(files, path.resolve(options.packagePath || defaultPackagePath(cwd, manifest)));
|
||||
|
||||
const packagePath = await getPackagePath(cwd, manifest, options);
|
||||
await writeVsix(files, path.resolve(packagePath));
|
||||
|
||||
return { manifest, packagePath, files };
|
||||
}
|
||||
|
@ -957,17 +998,17 @@ export async function packageCommand(options: IPackageOptions = {}): Promise<any
|
|||
/**
|
||||
* Lists the files included in the extension's package. Does not run prepublish.
|
||||
*/
|
||||
export function listFiles(cwd = process.cwd(), useYarn = false, packagedDependencies?: string[]): Promise<string[]> {
|
||||
export function listFiles(cwd = process.cwd(), useYarn = false, packagedDependencies?: string[], ignoreFile?: string): Promise<string[]> {
|
||||
return readManifest(cwd)
|
||||
.then(manifest => collectFiles(cwd, useYarn, packagedDependencies));
|
||||
.then(() => collectFiles(cwd, useYarn, packagedDependencies, ignoreFile));
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the files included in the extension's package. Runs prepublish.
|
||||
*/
|
||||
export function ls(cwd = process.cwd(), useYarn = false, packagedDependencies?: string[]): Promise<void> {
|
||||
export function ls(cwd = process.cwd(), useYarn = false, packagedDependencies?: string[], ignoreFile?: string): Promise<void> {
|
||||
return readManifest(cwd)
|
||||
.then(manifest => prepublish(cwd, manifest, useYarn))
|
||||
.then(manifest => collectFiles(cwd, useYarn, packagedDependencies))
|
||||
.then(() => collectFiles(cwd, useYarn, packagedDependencies, ignoreFile))
|
||||
.then(files => files.forEach(f => console.log(`${f}`)));
|
||||
}
|
||||
|
|
|
@ -1,19 +1,14 @@
|
|||
import { HttpClient, HttpClientResponse } from 'typed-rest-client/HttpClient';
|
||||
import {
|
||||
PublishedExtension, ExtensionQueryFlags, FilterCriteria, SortOrderType,
|
||||
SortByType, ExtensionQueryFilterType, TypeInfo
|
||||
} from 'azure-devops-node-api/interfaces/GalleryInterfaces';
|
||||
import { PublishedExtension, ExtensionQueryFlags, FilterCriteria, ExtensionQueryFilterType, TypeInfo } from 'azure-devops-node-api/interfaces/GalleryInterfaces';
|
||||
import { IHeaders } from 'azure-devops-node-api/interfaces/common/VsoBaseInterfaces';
|
||||
import { ContractSerializer } from 'azure-devops-node-api/Serialization';
|
||||
|
||||
export interface ExtensionQuery {
|
||||
pageNumber?: number;
|
||||
pageSize?: number;
|
||||
sortBy?: SortByType;
|
||||
sortOrder?: SortOrderType;
|
||||
flags?: ExtensionQueryFlags[];
|
||||
criteria?: FilterCriteria[];
|
||||
assetTypes?: string[];
|
||||
readonly pageNumber?: number;
|
||||
readonly pageSize?: number;
|
||||
readonly flags?: ExtensionQueryFlags[];
|
||||
readonly criteria?: FilterCriteria[];
|
||||
readonly assetTypes?: string[];
|
||||
}
|
||||
|
||||
export class PublicGalleryAPI {
|
||||
|
@ -29,8 +24,6 @@ export class PublicGalleryAPI {
|
|||
async extensionQuery({
|
||||
pageNumber = 1,
|
||||
pageSize = 1,
|
||||
sortBy = SortByType.Relevance,
|
||||
sortOrder = SortOrderType.Default,
|
||||
flags = [],
|
||||
criteria = [],
|
||||
assetTypes = [],
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import * as fs from 'fs';
|
||||
import { ExtensionQueryFlags, PublishedExtension, ExtensionQueryFilterType, PagingDirection, SortByType, SortOrderType } from 'azure-devops-node-api/interfaces/GalleryInterfaces';
|
||||
import { ExtensionQueryFlags, PublishedExtension } from 'azure-devops-node-api/interfaces/GalleryInterfaces';
|
||||
import { pack, readManifest, IPackage } from './package';
|
||||
import * as tmp from 'tmp';
|
||||
import { getPublisher } from './store';
|
||||
import { getGalleryAPI, read, getPublishedUrl, log } from './util';
|
||||
import { validatePublisher } from './validation';
|
||||
import { Manifest } from './manifest';
|
||||
import * as denodeify from 'denodeify';
|
||||
import * as yauzl from 'yauzl';
|
||||
|
@ -74,7 +73,7 @@ async function _publish(packagePath: string, pat: string, manifest: Manifest): P
|
|||
|
||||
return promise
|
||||
.catch(err => Promise.reject(err.statusCode === 409 ? `${fullName} already exists.` : err))
|
||||
.then(() => log.done(`Published ${fullName}\nYour extension will live at ${getPublishedUrl(name)} (might take a few seconds for it to show up).`));
|
||||
.then(() => log.done(`Published ${fullName}\nYour extension will live at ${getPublishedUrl(name)} (might take a few minutes for it to show up).`));
|
||||
})
|
||||
.catch(err => {
|
||||
const message = err && err.message || '';
|
||||
|
@ -97,13 +96,20 @@ export interface IPublishOptions {
|
|||
baseImagesUrl?: string;
|
||||
useYarn?: boolean;
|
||||
noVerify?: boolean;
|
||||
ignoreFile?: string;
|
||||
}
|
||||
|
||||
function versionBump(cwd: string = process.cwd(), version?: string, commitMessage?: string): Promise<void> {
|
||||
async function versionBump(cwd: string = process.cwd(), version?: string, commitMessage?: string): Promise<void> {
|
||||
if (!version) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
const manifest = await readManifest(cwd);
|
||||
|
||||
if (manifest.version === version) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (version) {
|
||||
case 'major':
|
||||
case 'minor':
|
||||
|
@ -127,14 +133,15 @@ function versionBump(cwd: string = process.cwd(), version?: string, commitMessag
|
|||
command = `${command} -m "${commitMessage}"`;
|
||||
}
|
||||
|
||||
// call `npm version` to do our dirty work
|
||||
return exec(command, { cwd })
|
||||
.then(({ stdout, stderr }) => {
|
||||
process.stdout.write(stdout);
|
||||
process.stderr.write(stderr);
|
||||
return Promise.resolve(null);
|
||||
})
|
||||
.catch(err => Promise.reject(err.message));
|
||||
try {
|
||||
// call `npm version` to do our dirty work
|
||||
const { stdout, stderr } = await exec(command, { cwd });
|
||||
process.stdout.write(stdout);
|
||||
process.stderr.write(stderr);
|
||||
return null;
|
||||
} catch (err) {
|
||||
throw err.message;
|
||||
}
|
||||
}
|
||||
|
||||
export function publish(options: IPublishOptions = {}): Promise<any> {
|
||||
|
@ -152,10 +159,11 @@ export function publish(options: IPublishOptions = {}): Promise<any> {
|
|||
const baseContentUrl = options.baseContentUrl;
|
||||
const baseImagesUrl = options.baseImagesUrl;
|
||||
const useYarn = options.useYarn;
|
||||
const ignoreFile = options.ignoreFile;
|
||||
|
||||
promise = versionBump(options.cwd, options.version, options.commitMessage)
|
||||
.then(() => tmpName())
|
||||
.then(packagePath => pack({ packagePath, cwd, baseContentUrl, baseImagesUrl, useYarn }));
|
||||
.then(packagePath => pack({ packagePath, cwd, baseContentUrl, baseImagesUrl, useYarn, ignoreFile }));
|
||||
}
|
||||
|
||||
return promise.then(({ manifest, packagePath }) => {
|
||||
|
@ -173,29 +181,33 @@ export function publish(options: IPublishOptions = {}): Promise<any> {
|
|||
|
||||
export interface IUnpublishOptions extends IPublishOptions {
|
||||
id?: string;
|
||||
force?: boolean;
|
||||
}
|
||||
|
||||
export function unpublish(options: IUnpublishOptions = {}): Promise<any> {
|
||||
let promise: Promise<{ publisher: string; name: string; }>;
|
||||
export async function unpublish(options: IUnpublishOptions = {}): Promise<any> {
|
||||
let publisher: string, name: string;
|
||||
|
||||
if (options.id) {
|
||||
const [publisher, name] = options.id.split('.');
|
||||
promise = Promise.resolve(({ publisher, name }));
|
||||
[publisher, name] = options.id.split('.');
|
||||
} else {
|
||||
promise = readManifest(options.cwd);
|
||||
const manifest = await readManifest(options.cwd);
|
||||
publisher = manifest.publisher;
|
||||
name = manifest.name;
|
||||
}
|
||||
|
||||
return promise.then(({ publisher, name }) => {
|
||||
const fullName = `${publisher}.${name}`;
|
||||
const pat = options.pat
|
||||
? Promise.resolve(options.pat)
|
||||
: getPublisher(publisher).then(p => p.pat);
|
||||
const fullName = `${publisher}.${name}`;
|
||||
|
||||
return read(`This will FOREVER delete '${fullName}'! Are you sure? [y/N] `)
|
||||
.then(answer => /^y$/i.test(answer) ? null : Promise.reject('Aborted'))
|
||||
.then(() => pat)
|
||||
.then(getGalleryAPI)
|
||||
.then(api => api.deleteExtension(publisher, name))
|
||||
.then(() => log.done(`Deleted extension: ${fullName}!`));
|
||||
});
|
||||
if (!options.force) {
|
||||
const answer = await read(`This will FOREVER delete '${fullName}'! Are you sure? [y/N] `);
|
||||
|
||||
if (!/^y$/i.test(answer)) {
|
||||
throw new Error('Aborted');
|
||||
}
|
||||
}
|
||||
|
||||
const pat = options.pat || (await getPublisher(publisher).then(p => p.pat));
|
||||
const api = await getGalleryAPI(pat);
|
||||
|
||||
await api.deleteExtension(publisher, name);
|
||||
log.done(`Deleted extension: ${fullName}!`);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { tableView, wordTrim } from './viewutils';
|
|||
|
||||
const pageSize = 100;
|
||||
|
||||
export async function search(searchText: string, json: boolean = false, pageNumber: number = 1): Promise<any> {
|
||||
export async function search(searchText: string, json: boolean = false): Promise<any> {
|
||||
const flags = [];
|
||||
const api = getPublicGalleryAPI();
|
||||
const results = await api.extensionQuery({
|
||||
|
|
|
@ -6,7 +6,7 @@ import { validatePublisher } from './validation';
|
|||
import * as denodeify from 'denodeify';
|
||||
|
||||
const readFile = denodeify<string, string, string>(fs.readFile);
|
||||
const writeFile = denodeify<string, string, void>(fs.writeFile as any);
|
||||
const writeFile = denodeify<string, string, object, void>(fs.writeFile as any);
|
||||
const storePath = path.join(home(), '.vsce');
|
||||
|
||||
export interface IPublisher {
|
||||
|
@ -40,7 +40,7 @@ function load(): Promise<IStore> {
|
|||
}
|
||||
|
||||
function save(store: IStore): Promise<IStore> {
|
||||
return writeFile(storePath, JSON.stringify(store))
|
||||
return writeFile(storePath, JSON.stringify(store), {mode: '0600'})
|
||||
.then(() => store);
|
||||
}
|
||||
|
||||
|
|
1
src/test/fixtures/readme/readme.expected.md
vendored
1
src/test/fixtures/readme/readme.expected.md
vendored
|
@ -21,6 +21,7 @@ The status bar lets you quickly navigate to any issue and you can see all positi
|
|||
[![Jump to issues](https://github.com/username/repository/raw/master/images/SpellMDDemo2.gif)](http://shouldnottouchthis/)
|
||||
[![Jump to issues](https://github.com/username/repository/raw/master/images/SpellMDDemo2.gif)](https://github.com/username/repository/blob/master/monkey)
|
||||
![](https://github.com/username/repository/raw/master/images/SpellMDDemo2.gif)
|
||||
<img src="https://github.com/username/repository/raw/master/images/myImage.gif">
|
||||
|
||||
The `spellMD.json` config file is watched so you can add more ignores or change mappings at will.
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ The status bar lets you quickly navigate to any issue and you can see all positi
|
|||
[![Jump to issues](https://github.com/username/repository/path/to/images/SpellMDDemo2.gif)](http://shouldnottouchthis/)
|
||||
[![Jump to issues](https://github.com/username/repository/path/to/images/SpellMDDemo2.gif)](https://github.com/username/repository/blob/master/monkey)
|
||||
![](https://github.com/username/repository/path/to/images/SpellMDDemo2.gif)
|
||||
<img src="https://github.com/username/repository/path/to/images/myImage.gif">
|
||||
|
||||
The `spellMD.json` config file is watched so you can add more ignores or change mappings at will.
|
||||
|
||||
|
|
1
src/test/fixtures/readme/readme.md
vendored
1
src/test/fixtures/readme/readme.md
vendored
|
@ -21,6 +21,7 @@ The status bar lets you quickly navigate to any issue and you can see all positi
|
|||
[![Jump to issues](images/SpellMDDemo2.gif)](http://shouldnottouchthis/)
|
||||
[![Jump to issues](images/SpellMDDemo2.gif)](monkey)
|
||||
![](images/SpellMDDemo2.gif)
|
||||
<img src="/images/myImage.gif">
|
||||
|
||||
The `spellMD.json` config file is watched so you can add more ignores or change mappings at will.
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ function _toVsixManifest(manifest: Manifest, files: IFile[]): Promise<string> {
|
|||
const assets = _.flatten(processors.map(p => p.assets));
|
||||
const vsix = processors.reduce((r, p) => ({ ...r, ...p.vsix }), { assets });
|
||||
|
||||
return toVsixManifest(assets, vsix);
|
||||
return toVsixManifest(vsix);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -359,7 +359,7 @@ describe('toVsixManifest', () => {
|
|||
};
|
||||
|
||||
const files = [
|
||||
{ path: 'extension/readme.md', contents: new Buffer('') }
|
||||
{ path: 'extension/readme.md', contents: Buffer.from('') }
|
||||
];
|
||||
|
||||
return _toVsixManifest(manifest, files)
|
||||
|
@ -381,7 +381,7 @@ describe('toVsixManifest', () => {
|
|||
};
|
||||
|
||||
const files = [
|
||||
{ path: 'extension/changelog.md', contents: new Buffer('') }
|
||||
{ path: 'extension/changelog.md', contents: Buffer.from('') }
|
||||
];
|
||||
|
||||
return _toVsixManifest(manifest, files)
|
||||
|
@ -1018,8 +1018,8 @@ describe('toVsixManifest', () => {
|
|||
};
|
||||
|
||||
const files = [
|
||||
{ path: 'extension/de.json', contents: new Buffer('') },
|
||||
{ path: 'extension/translations/pt.json', contents: new Buffer('') }
|
||||
{ path: 'extension/de.json', contents: Buffer.from('') },
|
||||
{ path: 'extension/translations/pt.json', contents: Buffer.from('') }
|
||||
];
|
||||
|
||||
return _toVsixManifest(manifest, files)
|
||||
|
@ -1551,6 +1551,33 @@ describe('MarkdownProcessor', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should not replace issue links with urls if its a github repo but issue link expansion is disabled.', () => {
|
||||
const manifest = {
|
||||
name: 'test',
|
||||
publisher: 'mocha',
|
||||
version: '0.0.1',
|
||||
description: 'test extension',
|
||||
engines: Object.create(null),
|
||||
repository: 'https://github.com/username/repository.git'
|
||||
};
|
||||
|
||||
const root = fixture('readme');
|
||||
const processor = new ReadmeProcessor(manifest, { expandGitHubIssueLinks: false });
|
||||
const readme = {
|
||||
path: 'extension/readme.md',
|
||||
localPath: path.join(root, 'readme.github.md')
|
||||
};
|
||||
|
||||
return processor.onFile(readme)
|
||||
.then(file => read(file))
|
||||
.then(actual => {
|
||||
return readFile(path.join(root, 'readme.github.md'), 'utf8')
|
||||
.then(expected => {
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not replace issue links with urls if its not a github repo.', () => {
|
||||
const manifest = {
|
||||
name: 'test',
|
||||
|
|
|
@ -109,6 +109,7 @@ describe('validateVSCodeTypesCompatibility', () => {
|
|||
|
||||
validateVSCodeTypesCompatibility('1.30.0', '1.30.0');
|
||||
validateVSCodeTypesCompatibility('1.30.0', '1.20.0');
|
||||
validateVSCodeTypesCompatibility('1.46.0', '1.45.1');
|
||||
|
||||
assert.throws(() => validateVSCodeTypesCompatibility('1.30.0', '1.40.0'));
|
||||
assert.throws(() => validateVSCodeTypesCompatibility('1.30.0', '^1.40.0'));
|
||||
|
|
|
@ -91,13 +91,13 @@ export function validateVSCodeTypesCompatibility(engineVersion: string, typeVers
|
|||
|
||||
const error = new Error(`@types/vscode ${typeVersion} greater than engines.vscode ${engineVersion}. Consider upgrade engines.vscode or use an older @types/vscode version`);
|
||||
|
||||
if (typeof typeMajor === 'number' && typeof engineMajor === 'number' && typeMajor > engineMajor) {
|
||||
if (typeMajor > engineMajor) {
|
||||
throw error;
|
||||
}
|
||||
if (typeof typeMinor === 'number' && typeof engineMinor === 'number' && typeMinor > engineMinor) {
|
||||
if (typeMajor === engineMajor && typeMinor > engineMinor) {
|
||||
throw error;
|
||||
}
|
||||
if (typeof typePatch === 'number' && typeof enginePatch === 'number' && typePatch > enginePatch) {
|
||||
if (typeMajor === engineMajor && typeMinor === engineMinor && typePatch > enginePatch) {
|
||||
throw error;
|
||||
}
|
||||
}
|
|
@ -13,16 +13,9 @@ const columns = process.stdout.columns ? process.stdout.columns : 80;
|
|||
// unicode charset. For now we use fallback icons when on windows.
|
||||
const useFallbackIcons = process.platform === 'win32';
|
||||
|
||||
export const icons = useFallbackIcons?
|
||||
{
|
||||
download: '\u{2193}',
|
||||
star: '\u{2665}',
|
||||
emptyStar: '\u{2022}',
|
||||
} : {
|
||||
download: '\u{2913}',
|
||||
star: '\u{2605}',
|
||||
emptyStar: '\u{2606}',
|
||||
};
|
||||
export const icons = useFallbackIcons
|
||||
? { download: '\u{2193}', star: '\u{2665}', emptyStar: '\u{2022}', }
|
||||
: { download: '\u{2913}', star: '\u{2605}', emptyStar: '\u{2606}', };
|
||||
|
||||
export function formatDate(date) { return date.toLocaleString(fixedLocale, format.date); }
|
||||
export function formatTime(date) { return date.toLocaleString(fixedLocale, format.time); }
|
||||
|
@ -53,11 +46,11 @@ export function wordWrap(text: string, width: number = columns): string {
|
|||
return text
|
||||
.replace(/^\s+/, '')
|
||||
.split('')
|
||||
.reduce(([out, buffer, pos], ch, i) => {
|
||||
.reduce(([out, buffer, pos], ch) => {
|
||||
const nl = pos === maxWidth ? `\n${indent}` : '';
|
||||
const newPos: number = nl ? 0 : +pos + 1;
|
||||
return / |-|,|\./.test(ch) ?
|
||||
[`${out}${buffer}${ch}${nl}`, '', newPos] : [`${out}${nl}`, buffer+ch, newPos];
|
||||
[`${out}${buffer}${ch}${nl}`, '', newPos] : [`${out}${nl}`, buffer + ch, newPos];
|
||||
}, [indent, '', 0])
|
||||
.slice(0, 2)
|
||||
.join('');
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
--require source-map-support/register out/test
|
|
@ -3,7 +3,9 @@
|
|||
"target": "es2015",
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"outDir": "out"
|
||||
"outDir": "out",
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
|
|
Loading…
Add table
Reference in a new issue