mirror of
https://github.com/chenasraf/DefinitelyTyped-tools.git
synced 2026-05-18 01:49:03 +00:00
Make every step idempotent
This commit is contained in:
committed by
Andrew Branch
parent
9798dd3b97
commit
15632c04c9
@@ -1,12 +1,27 @@
|
||||
## Workflow
|
||||
|
||||
There are four steps in the typings publish process
|
||||
|
||||
### Parse
|
||||
|
||||
The *parse* step parses each folder in the source repo.
|
||||
This generates a `types-data.json` file.
|
||||
|
||||
### Search
|
||||
|
||||
The *search* step generates search metadata from the `types-data.json` file.
|
||||
|
||||
### Generate
|
||||
|
||||
The *generate* step generates NPM packages on disk.
|
||||
This step increments version numbers in `versions.json` if the content in the originating folder has changed.
|
||||
|
||||
### Publish
|
||||
|
||||
The *publish* step publishes all generated packages to NPM.
|
||||
|
||||
## Commandline arguments
|
||||
|
||||
### `--skipPublish`
|
||||
|
||||
Passing `--skipPublish` causes the `npm publish` step to be skipped.
|
||||
Version numbers in `version.json` are subsequently not changed.
|
||||
|
||||
This is useful for debugging purposes.
|
||||
|
||||
### `--forceUpdate`
|
||||
|
||||
Passing `--forceUpdate` causes all version checks to be treated as "needs update".
|
||||
|
||||
29
packages/types-publisher/package.json
Normal file
29
packages/types-publisher/package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "publish-typings",
|
||||
"version": "0.6.0",
|
||||
"description": "Publish DefinitelyTyped definitions to NPM",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
"publish-typings": "createSearchIndex.js"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"typescript": "^1.9.0-dev.20160331"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "node node_modules/typescript/lib/tsc.js",
|
||||
"generate-search": "npm run build && npm run _generate-search",
|
||||
"_generate-search": "node bin/createSearchIndex.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/RyanCavanaugh/publish-typings.git"
|
||||
},
|
||||
"author": "Microsoft",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/RyanCavanaugh/publish-typings/issues"
|
||||
},
|
||||
"homepage": "https://github.com/RyanCavanaugh/publish-typings#readme"
|
||||
}
|
||||
22
packages/types-publisher/src/check-parse-results.ts
Normal file
22
packages/types-publisher/src/check-parse-results.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
function detectProjectAndLibraryNameDuplicates() {
|
||||
check(info => info.libraryName, 'Library Name');
|
||||
check(info => info.projectName, 'Project Name');
|
||||
|
||||
function check(func: (info: TypingsData) => string, key: string) {
|
||||
const lookup: { [libName: string]: string[] } = {};
|
||||
infos.forEach(info => {
|
||||
const name = func(info);
|
||||
if (name !== undefined) {
|
||||
(lookup[name] || (lookup[name] = [])).push(info.typingsPackageName);
|
||||
}
|
||||
});
|
||||
for (const k of Object.keys(lookup)) {
|
||||
if (lookup[k].length > 1) {
|
||||
warningLog.push(` * Duplicate ${key} descriptions "${k}"`);
|
||||
lookup[k].forEach(n => warningLog.push(` * ${n}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
packages/types-publisher/src/create-search-index.ts
Normal file
55
packages/types-publisher/src/create-search-index.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
// https://api.npmjs.org/downloads/point/last-month/jquery,express,flarp,react
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as request from 'request';
|
||||
|
||||
const rawData: SearchRecord[] = JSON.parse(fs.readFileSync('search-raw.json', 'utf-8'));
|
||||
|
||||
searchData.push({
|
||||
packageName: info.data.projectName,
|
||||
libraryName: info.data.libraryName,
|
||||
globals: info.data.globals,
|
||||
npmPackageName: info.data.typingsPackageName,
|
||||
typePackageName: info.data.typingsPackageName,
|
||||
declaredExternalModules: info.data.declaredModules
|
||||
});
|
||||
|
||||
interface NpmResult {
|
||||
[packageName: string]: {
|
||||
downloads: number;
|
||||
}
|
||||
}
|
||||
|
||||
function getDownloadCounts(done: () => void) {
|
||||
function next() {
|
||||
const unchecked = rawData.filter(r => (r.npmPackageName !== undefined) && (r.downloads === undefined));
|
||||
if (unchecked.length === 0) {
|
||||
done();
|
||||
} else {
|
||||
// Unknown: How many can we query at once?
|
||||
const nextToCheck = unchecked.slice(0, 200);
|
||||
const url = 'https://api.npmjs.org/downloads/point/last-month/' + nextToCheck.map(r => r.npmPackageName).join(',');
|
||||
request.get(url, (err: any, resp: any, data: string) => {
|
||||
const json = JSON.parse(data);
|
||||
if (err) throw err;
|
||||
nextToCheck.forEach(r => {
|
||||
const result = json[r.npmPackageName];
|
||||
r.downloads = result ? result.downloads : 0;
|
||||
});
|
||||
next();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
|
||||
function main() {
|
||||
getDownloadCounts(() => {
|
||||
rawData.sort((a, b) => a.downloads - b.downloads);
|
||||
fs.writeFileSync('search-with-downloads.json', JSON.stringify(rawData, undefined, 4), 'utf-8');
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,179 +0,0 @@
|
||||
import { TypingsData, DefinitionFileKind } from './definition-parser';
|
||||
import * as fs from 'fs';
|
||||
import * as crypto from 'crypto';
|
||||
import * as path from 'path';
|
||||
import * as child_process from 'child_process';
|
||||
|
||||
const settings: PublishSettings = JSON.parse(fs.readFileSync('./settings.json', 'utf-8'));
|
||||
|
||||
namespace Versions {
|
||||
const versionFilename = 'versions.json';
|
||||
|
||||
interface VersionMap {
|
||||
[typingsPackageName: string]: {
|
||||
lastVersion: number;
|
||||
lastContentHash: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function performUpdate(key: string, content: string, update: (version: number) => boolean) {
|
||||
let data: VersionMap = fs.existsSync(versionFilename) ? JSON.parse(fs.readFileSync(versionFilename, 'utf-8')) : {};
|
||||
|
||||
const forceUpdate = process.argv.some(arg => arg === '--forceUpdate');
|
||||
|
||||
const hashValue = computeHash(key);
|
||||
let entry = data[key];
|
||||
|
||||
if (entry === undefined) {
|
||||
data[key] = entry = { lastVersion: 0, lastContentHash: '' };
|
||||
}
|
||||
|
||||
if (entry.lastContentHash !== hashValue || forceUpdate) {
|
||||
const vNext = entry.lastVersion + (forceUpdate ? 2 : 1);
|
||||
|
||||
if(update(vNext)) {
|
||||
data[key] = { lastVersion: vNext, lastContentHash: hashValue };
|
||||
fs.writeFileSync(versionFilename, JSON.stringify(data, undefined, 4));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function computeHash(content: string) {
|
||||
const h = crypto.createHash('sha256');
|
||||
h.update(content, 'utf-8');
|
||||
return h.digest('base64');
|
||||
}
|
||||
}
|
||||
|
||||
function mkdir(p: string) {
|
||||
try {
|
||||
fs.statSync(p);
|
||||
} catch(e) {
|
||||
fs.mkdirSync(p);
|
||||
}
|
||||
}
|
||||
|
||||
function patchDefinitionFile(input: string): string {
|
||||
const pathToLibrary = /\/\/\/ <reference path="..\/(\w.+)\/.+"/gm;
|
||||
let output = input.replace(pathToLibrary, '/// <reference library="$1"');
|
||||
return output;
|
||||
}
|
||||
|
||||
export function publish(typing: TypingsData): { log: string[] } {
|
||||
const log: string[] = [];
|
||||
|
||||
log.push(`Possibly publishing ${typing.libraryName}`);
|
||||
|
||||
let allContent = '';
|
||||
// Make the file ordering deterministic so the hash doesn't jump around for no reason
|
||||
typing.files.sort();
|
||||
for(const file of typing.files) {
|
||||
allContent = allContent + fs.readFileSync(path.join(typing.root, file), 'utf-8');
|
||||
}
|
||||
|
||||
const actualPackageName = typing.packageName.toLowerCase();
|
||||
|
||||
const didUpdate = Versions.performUpdate(actualPackageName, allContent, version => {
|
||||
log.push('Generate package.json and README.md; ensure output path exists');
|
||||
const packageJson = JSON.stringify(createPackageJSON(typing, version), undefined, 4);
|
||||
const readme = createReadme(typing);
|
||||
|
||||
const outputPath = path.join(settings.outputPath, actualPackageName);
|
||||
mkdir(outputPath);
|
||||
|
||||
fs.writeFileSync(path.join(outputPath, 'package.json'), packageJson, 'utf-8');
|
||||
fs.writeFileSync(path.join(outputPath, 'README.md'), readme, 'utf-8');
|
||||
|
||||
typing.files.forEach(file => {
|
||||
log.push(`Copy and patch ${file}`);
|
||||
let content = fs.readFileSync(path.join(typing.root, file), 'utf-8');
|
||||
content = patchDefinitionFile(content);
|
||||
fs.writeFileSync(path.join(outputPath, file), content);
|
||||
});
|
||||
|
||||
const args: string[] = ['npm', 'publish', path.resolve(outputPath), '--access public'];
|
||||
if (settings.tag) {
|
||||
args.push(`--tag ${settings.tag}`);
|
||||
}
|
||||
|
||||
const cmd = args.join(' ');
|
||||
log.push(`Run ${cmd}`);
|
||||
try {
|
||||
const skipPublish = process.argv.some(arg => arg === '--skipPublish');
|
||||
if (skipPublish) return false;
|
||||
|
||||
const result = <string>child_process.execSync(cmd, { encoding: 'utf-8' });
|
||||
log.push(`Ran successfully`);
|
||||
log.push(result);
|
||||
return true;
|
||||
} catch(e) {
|
||||
log.push(`!!! Publish failed`);
|
||||
log.push(JSON.stringify(e));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (!didUpdate) {
|
||||
log.push('Package was already up-to-date');
|
||||
}
|
||||
|
||||
return { log };
|
||||
}
|
||||
|
||||
|
||||
function createPackageJSON(typing: TypingsData, fileVersion: number) {
|
||||
const dependencies: any = {};
|
||||
typing.moduleDependencies.forEach(d => dependencies[d] = '*');
|
||||
typing.libraryDependencies.forEach(d => dependencies[`@${settings.scopeName}/${d}`] = '*');
|
||||
|
||||
let version = `${typing.libraryMajorVersion}.${typing.libraryMinorVersion}.${fileVersion}`;
|
||||
if (settings.prereleaseTag) {
|
||||
version = `${version}-${settings.prereleaseTag}`;
|
||||
}
|
||||
|
||||
return ({
|
||||
name: `@${settings.scopeName}/${typing.packageName.toLowerCase()}`,
|
||||
version,
|
||||
description: `Type definitions for ${typing.libraryName} from ${typing.sourceRepoURL}`,
|
||||
main: '',
|
||||
scripts: {},
|
||||
author: typing.authors,
|
||||
license: 'MIT',
|
||||
typings: typing.definitionFilename,
|
||||
dependencies
|
||||
});
|
||||
}
|
||||
|
||||
function createReadme(typing: TypingsData) {
|
||||
const lines: string[] = [];
|
||||
|
||||
lines.push(`This package contains type definitions for ${typing.libraryName}.`)
|
||||
if (typing.projectName) {
|
||||
lines.push('');
|
||||
lines.push(`The project URL or description is ${typing.projectName}`);
|
||||
}
|
||||
|
||||
if (typing.authors) {
|
||||
lines.push('');
|
||||
lines.push(`These definitions were written by ${typing.authors}.`);
|
||||
}
|
||||
|
||||
lines.push('');
|
||||
lines.push(`Typings were exported from ${typing.sourceRepoURL} in the ${typing.packageName} directory.`);
|
||||
|
||||
lines.push('');
|
||||
lines.push(`Additional Details`)
|
||||
lines.push(` * Last updated: ${(new Date()).toUTCString()}`);
|
||||
lines.push(` * Typings kind: ${typing.kind}`);
|
||||
lines.push(` * Library Dependencies: ${typing.libraryDependencies.length ? typing.libraryDependencies.join(', ') : 'none'}`);
|
||||
lines.push(` * Module Dependencies: ${typing.moduleDependencies.length ? typing.moduleDependencies.join(', ') : 'none'}`);
|
||||
lines.push(` * Global values: ${typing.globals.length ? typing.globals.join(', ') : 'none'}`);
|
||||
lines.push('');
|
||||
|
||||
return lines.join('\r\n');
|
||||
}
|
||||
|
||||
17
packages/types-publisher/src/generate-packages.ts
Normal file
17
packages/types-publisher/src/generate-packages.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import * as common from './lib/common';
|
||||
import * as generator from './lib/package-generator';
|
||||
|
||||
const typeData = <common.TypesDataFile>common.readDataFile(common.typesDataFilename);
|
||||
|
||||
if (typeData === undefined) {
|
||||
console.log('Run parse-definitions first!');
|
||||
} else {
|
||||
const log: string[] = [];
|
||||
Object.keys(typeData).forEach(packageName => {
|
||||
const typing = typeData[packageName];
|
||||
const result = generator.generatePackage(typing);
|
||||
log.push(` * ${packageName}`);
|
||||
result.log.forEach(line => log.push(` * ${line}`));
|
||||
});
|
||||
common.writeLogSync('package-generator.md', log);
|
||||
}
|
||||
149
packages/types-publisher/src/lib/common.ts
Normal file
149
packages/types-publisher/src/lib/common.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import path = require('path');
|
||||
import fs = require('fs');
|
||||
import crypto = require('crypto');
|
||||
|
||||
export const home = path.join(__dirname, '..', '..');
|
||||
export const settings: PublishSettings = JSON.parse(fs.readFileSync(path.join(home, 'settings.json'), 'utf-8'));
|
||||
export const typesDataFilename = 'types-data.json';
|
||||
export const versionsFilename = 'versions.json';
|
||||
|
||||
|
||||
export interface TypesDataFile {
|
||||
[folderName: string]: TypingsData;
|
||||
}
|
||||
|
||||
export interface TypingsData {
|
||||
kind: string;
|
||||
|
||||
moduleDependencies: string[];
|
||||
libraryDependencies: string[];
|
||||
|
||||
// e.g. https://github.com/DefinitelyTyped
|
||||
sourceRepoURL: string;
|
||||
|
||||
// The name of the primary definition file, e.g. 'jquery.d.ts'
|
||||
definitionFilename: string;
|
||||
|
||||
// The name of the library (human readable, e.g. might be 'Moment.js' even though packageName is 'moment')
|
||||
libraryName: string;
|
||||
|
||||
// The NPM name to publish this under, e.g. 'jquery'. May not be lower-cased yet.
|
||||
typingsPackageName: string;
|
||||
|
||||
// Parsed from 'Definitions by:'
|
||||
authors: string;
|
||||
|
||||
// Optionally-present name or URL of the project, e.g. 'http://cordova.apache.org'
|
||||
projectName: string;
|
||||
|
||||
// Names introduced into the global scope by this definition set
|
||||
globals: string[];
|
||||
|
||||
// External modules declared by this package. Includes the containing folder name when applicable (e.g. proper module)
|
||||
declaredModules: string[];
|
||||
|
||||
// The major version of the library (e.g. '1' for 1.0, '2' for 2.0)
|
||||
libraryMajorVersion: string;
|
||||
// The minor version of the library
|
||||
libraryMinorVersion: string;
|
||||
|
||||
// The full path to the containing folder of all files, e.g. 'C:/github/DefinitelyTyped'
|
||||
root: string;
|
||||
|
||||
// Files that should be published with this definition, e.g. ['jquery.d.ts', 'jquery-extras.d.ts']
|
||||
files: string[];
|
||||
|
||||
// A hash computed from all files from this definition
|
||||
contentHash: string;
|
||||
}
|
||||
|
||||
export enum DefinitionFileKind {
|
||||
// Dunno
|
||||
Unknown,
|
||||
// UMD module file
|
||||
UMD,
|
||||
// File has global variables or interfaces, but not any external modules
|
||||
Global,
|
||||
// File has top-level export declarations
|
||||
ProperModule,
|
||||
// File has a single declare module "foo" but no global interfaces or variables
|
||||
DeclareModule,
|
||||
// Some combination of Global and DeclareModule
|
||||
Mixed,
|
||||
// More than one 'declare module "foo"''
|
||||
MultipleModules,
|
||||
// Augments an external module
|
||||
ModuleAugmentation,
|
||||
// Old-style UMD
|
||||
OldUMD
|
||||
}
|
||||
|
||||
export enum RejectionReason {
|
||||
TooManyFiles,
|
||||
BadFileFormat,
|
||||
ReferencePaths
|
||||
}
|
||||
|
||||
export interface TypingParseFailResult {
|
||||
rejectionReason: RejectionReason;
|
||||
log: string[];
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export interface TypingParseSucceedResult {
|
||||
data: TypingsData;
|
||||
log: string[];
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export function isSuccess(t: TypingParseSucceedResult | TypingParseFailResult): t is TypingParseSucceedResult {
|
||||
return (t as TypingParseSucceedResult).data !== undefined;
|
||||
}
|
||||
|
||||
export function isFail(t: TypingParseSucceedResult | TypingParseFailResult): t is TypingParseFailResult {
|
||||
return (t as TypingParseFailResult).rejectionReason !== undefined;
|
||||
}
|
||||
|
||||
export function mkdir(p: string) {
|
||||
try {
|
||||
fs.statSync(p);
|
||||
} catch (e) {
|
||||
fs.mkdirSync(p);
|
||||
}
|
||||
}
|
||||
|
||||
export function writeLogSync(logName: string, contents: string[]) {
|
||||
const logDir = path.join(home, 'logs');
|
||||
mkdir(logDir);
|
||||
fs.writeFileSync(path.join(logDir, logName), contents.join('\r\n'), 'utf-8');
|
||||
}
|
||||
|
||||
export function writeDataFile(filename: string, content: {}) {
|
||||
const dataDir = path.join(home, 'data');
|
||||
mkdir(dataDir);
|
||||
if (typeof content !== 'string') {
|
||||
content = JSON.stringify(content, undefined, 4);
|
||||
}
|
||||
fs.writeFileSync(path.join(dataDir, filename), content, 'utf-8');
|
||||
}
|
||||
|
||||
export function readDataFile(filename: string): {} {
|
||||
const dataDir = path.join(home, 'data');
|
||||
const fullPath = path.join(dataDir, filename);
|
||||
if (fs.existsSync(fullPath)) {
|
||||
return JSON.parse(fs.readFileSync(fullPath, 'utf-8'));
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function computeHash(content: string) {
|
||||
const h = crypto.createHash('sha256');
|
||||
h.update(content, 'utf-8');
|
||||
return <string>h.digest('hex');
|
||||
}
|
||||
|
||||
export function getOutputPath(typing: TypingsData) {
|
||||
const outputPath = path.join(settings.outputPath, typing.typingsPackageName);
|
||||
return outputPath;
|
||||
}
|
||||
@@ -2,88 +2,10 @@ import * as ts from 'typescript';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
export enum DefinitionFileKind {
|
||||
// Dunno
|
||||
Unknown,
|
||||
// UMD module file
|
||||
UMD,
|
||||
// File has global variables or interfaces, but not any external modules
|
||||
Global,
|
||||
// File has top-level export declarations
|
||||
ProperModule,
|
||||
// File has a single declare module "foo" but no global interfaces or variables
|
||||
DeclareModule,
|
||||
// Some combination of Global and DeclareModule
|
||||
Mixed,
|
||||
// More than one 'declare module "foo"''
|
||||
MultipleModules,
|
||||
// Augments an external module
|
||||
ModuleAugmentation,
|
||||
// Old-style UMD
|
||||
OldUMD
|
||||
}
|
||||
import { TypingsData, DefinitionFileKind, RejectionReason, TypingParseSucceedResult, TypingParseFailResult, computeHash } from './common';
|
||||
|
||||
export enum RejectionReason {
|
||||
TooManyFiles,
|
||||
BadFileFormat,
|
||||
ReferencePaths
|
||||
}
|
||||
|
||||
export interface TypingParseFailResult {
|
||||
rejectionReason: RejectionReason;
|
||||
log: string[];
|
||||
}
|
||||
|
||||
export interface TypingParseSucceedResult {
|
||||
data: TypingsData;
|
||||
log: string[];
|
||||
}
|
||||
|
||||
export function isSuccess(t: TypingParseSucceedResult | TypingParseFailResult): t is TypingParseSucceedResult {
|
||||
return (t as TypingParseSucceedResult).data !== undefined;
|
||||
}
|
||||
|
||||
export function isFail(t: TypingParseSucceedResult | TypingParseFailResult): t is TypingParseFailResult {
|
||||
return (t as TypingParseFailResult).rejectionReason !== undefined;
|
||||
}
|
||||
|
||||
export interface TypingsData {
|
||||
kind: string;
|
||||
|
||||
moduleDependencies: string[];
|
||||
libraryDependencies: string[];
|
||||
|
||||
// e.g. https://github.com/DefinitelyTyped
|
||||
sourceRepoURL: string;
|
||||
|
||||
// The name of the primary definition file, e.g. 'jquery.d.ts'
|
||||
definitionFilename: string;
|
||||
|
||||
// The name of the library (human readable, e.g. might be 'Moment.js' even though packageName is 'moment')
|
||||
libraryName: string;
|
||||
|
||||
// The NPM name to publish this under, e.g. 'jquery'. May not be lower-cased yet.
|
||||
packageName: string;
|
||||
|
||||
// Parsed from 'Definitions by:'
|
||||
authors: string;
|
||||
|
||||
// Optionally-present name or URL of the project, e.g. 'http://cordova.apache.org'
|
||||
projectName: string;
|
||||
|
||||
// Names introduced into the global scope by this definition set
|
||||
globals: string[];
|
||||
|
||||
// The major version of the library (e.g. '1' for 1.0, '2' for 2.0)
|
||||
libraryMajorVersion: string;
|
||||
// The minor version of the library
|
||||
libraryMinorVersion: string;
|
||||
|
||||
// The full path to the containing folder of all files, e.g. 'C:/github/DefinitelyTyped'
|
||||
root: string;
|
||||
|
||||
// Files that should be published with this definition, e.g. ['jquery.d.ts', 'jquery-extras.d.ts']
|
||||
files: string[];
|
||||
function stripQuotes(s: string) {
|
||||
return s.substr(1, s.length - 2);
|
||||
}
|
||||
|
||||
const augmentedGlobals = ['Array', ' Function', 'String', 'Number', 'Window', 'Date', 'StringConstructor', 'NumberConstructor', 'Math', 'HTMLElement'];
|
||||
@@ -107,10 +29,6 @@ function isSupportedFileKind(kind: DefinitionFileKind) {
|
||||
}
|
||||
}
|
||||
|
||||
function stripQuotes(s: string) {
|
||||
return s.substr(1, s.length - 2);
|
||||
}
|
||||
|
||||
enum DeclarationFlags {
|
||||
None = 0,
|
||||
Value = 1 << 0,
|
||||
@@ -153,6 +71,8 @@ function getNamespaceFlags(ns: ts.ModuleDeclaration): DeclarationFlags {
|
||||
|
||||
export function getTypingInfo(directory: string): TypingParseFailResult | TypingParseSucceedResult {
|
||||
const log: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
const folderName = path.basename(directory);
|
||||
|
||||
log.push(`Reading contents of ${directory}`);
|
||||
const files = fs.readdirSync(directory);
|
||||
@@ -162,12 +82,10 @@ export function getTypingInfo(directory: string): TypingParseFailResult | Typing
|
||||
// * -tests.ts (tests)
|
||||
// * .d.ts.tscparams (for testing)
|
||||
|
||||
// "// Type definitions for JSFL v3.2"
|
||||
|
||||
log.push(`Found ${files.length} files`);
|
||||
|
||||
const declFiles = files.filter(f => /\.d\.ts$/.test(f));
|
||||
const candidates = [path.basename(directory) + ".d.ts", "index.d.ts"];
|
||||
const candidates = [folderName + ".d.ts", "index.d.ts"];
|
||||
log.push(`Found ${declFiles.length} .d.ts files (${declFiles.join(', ')})`);
|
||||
|
||||
let entryPointFilename: string;
|
||||
@@ -184,10 +102,12 @@ export function getTypingInfo(directory: string): TypingParseFailResult | Typing
|
||||
}
|
||||
}
|
||||
}
|
||||
declFiles.sort();
|
||||
|
||||
if (entryPointFilename === undefined) {
|
||||
log.push('Exiting, found either zero or more than one .d.ts file and none of ' + candidates.join(' or '));
|
||||
return { log, rejectionReason: RejectionReason.TooManyFiles };
|
||||
warnings.push('Found either zero or more than one .d.ts file and none of ' + candidates.join(' or '));
|
||||
return { log, warnings, rejectionReason: RejectionReason.TooManyFiles };
|
||||
}
|
||||
const entryPointContent = readFile(entryPointFilename);
|
||||
|
||||
@@ -198,6 +118,7 @@ export function getTypingInfo(directory: string): TypingParseFailResult | Typing
|
||||
|
||||
const moduleDependencies: string[] = [];
|
||||
const referencedLibraries: string[] = [];
|
||||
const declaredModules: string[] = [];
|
||||
|
||||
let globalSymbols: { [name: string]: ts.SymbolFlags } = {};
|
||||
function recordSymbol(name: string, flags: DeclarationFlags) {
|
||||
@@ -257,7 +178,9 @@ export function getTypingInfo(directory: string): TypingParseFailResult | Typing
|
||||
} else {
|
||||
const nameKind = (node as ts.ModuleDeclaration).name.kind;
|
||||
if (nameKind === ts.SyntaxKind.StringLiteral) {
|
||||
log.push(`Found ambient external module ${(node as ts.ModuleDeclaration).name.getText()}`);
|
||||
const name = stripQuotes((node as ts.ModuleDeclaration).name.getText());
|
||||
declaredModules.push(name);
|
||||
log.push(`Found ambient external module ${name}`);
|
||||
ambientModuleCount++;
|
||||
} else {
|
||||
const moduleName = (node as ts.ModuleDeclaration).name.getText();
|
||||
@@ -374,9 +297,18 @@ export function getTypingInfo(directory: string): TypingParseFailResult | Typing
|
||||
}
|
||||
}
|
||||
|
||||
if (declaredModules.length === 1 && fileKind !== DefinitionFileKind.ModuleAugmentation && declaredModules[0].toLowerCase() !== folderName.toLowerCase()) {
|
||||
warnings.push(`Declared module \`${declaredModules[0]}\` is in folder with incorrect name \`${folderName}\``);
|
||||
}
|
||||
|
||||
if (declaredModules.length === 0 && fileKind === DefinitionFileKind.ProperModule) {
|
||||
declaredModules.push(folderName);
|
||||
}
|
||||
|
||||
if (!isSupportedFileKind(fileKind)) {
|
||||
log.push(`Exiting, ${DefinitionFileKind[fileKind]} is not a supported file kind`);
|
||||
return { log, rejectionReason: RejectionReason.BadFileFormat };
|
||||
warnings.push(`${DefinitionFileKind[fileKind]} is not a supported file kind`);
|
||||
return { log, warnings, rejectionReason: RejectionReason.BadFileFormat };
|
||||
}
|
||||
|
||||
function regexMatch(rx: RegExp, defaultValue: string): string {
|
||||
@@ -393,11 +325,14 @@ export function getTypingInfo(directory: string): TypingParseFailResult | Typing
|
||||
const sourceRepoURL = 'https://www.github.com/DefinitelyTyped/DefinitelyTyped';
|
||||
|
||||
if (packageName !== packageName.toLowerCase()) {
|
||||
log.push(`!!! WARNING: ${packageName} !== ${packageName.toLowerCase()}`);
|
||||
warnings.push(`Package name ${packageName} should be strictly lowercase`);
|
||||
}
|
||||
|
||||
const allContent = declFiles.map(d => d + '**' + readFile(d)).join('||');
|
||||
|
||||
return {
|
||||
log,
|
||||
warnings,
|
||||
data: {
|
||||
authors,
|
||||
definitionFilename: entryPointFilename,
|
||||
@@ -406,13 +341,15 @@ export function getTypingInfo(directory: string): TypingParseFailResult | Typing
|
||||
libraryMajorVersion,
|
||||
libraryMinorVersion,
|
||||
libraryName,
|
||||
packageName,
|
||||
typingsPackageName: folderName.toLowerCase(),
|
||||
projectName,
|
||||
sourceRepoURL,
|
||||
kind: DefinitionFileKind[fileKind],
|
||||
globals: Object.keys(globalSymbols).filter(k => !!(globalSymbols[k] & DeclarationFlags.Value)),
|
||||
declaredModules,
|
||||
root: path.resolve(directory),
|
||||
files: declFiles
|
||||
files: declFiles,
|
||||
contentHash: computeHash(allContent)
|
||||
}
|
||||
};
|
||||
|
||||
153
packages/types-publisher/src/lib/package-generator.ts
Normal file
153
packages/types-publisher/src/lib/package-generator.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import { TypingsData, DefinitionFileKind, mkdir, settings, getOutputPath } from './common';
|
||||
import * as fs from 'fs';
|
||||
import * as crypto from 'crypto';
|
||||
import * as path from 'path';
|
||||
import * as child_process from 'child_process';
|
||||
import * as request from 'request';
|
||||
|
||||
export function generatePackage(typing: TypingsData): { log: string[] } {
|
||||
const log: string[] = [];
|
||||
|
||||
const fileVersion = Versions.computeVersion(typing);
|
||||
|
||||
const outputPath = getOutputPath(typing);
|
||||
log.push(`Create output path ${outputPath}`);
|
||||
mkdir(outputPath);
|
||||
|
||||
log.push(`Clear out old files`);
|
||||
fs.readdirSync(outputPath).forEach(file => {
|
||||
fs.unlinkSync(path.join(outputPath, file));
|
||||
});
|
||||
|
||||
log.push('Generate package.json, metadata.json, and README.md');
|
||||
const packageJson = createPackageJSON(typing, fileVersion);
|
||||
const metadataJson = createMetadataJSON(typing);
|
||||
const readme = createReadme(typing);
|
||||
|
||||
log.push('Write metadata files to disk');
|
||||
writeOutputFile('package.json', packageJson);
|
||||
writeOutputFile('types-metadata.json', metadataJson);
|
||||
writeOutputFile('README.md', readme);
|
||||
|
||||
typing.files.forEach(file => {
|
||||
log.push(`Copy and patch ${file}`);
|
||||
let content = fs.readFileSync(path.join(typing.root, file), 'utf-8');
|
||||
content = patchDefinitionFile(content);
|
||||
writeOutputFile(file, content);
|
||||
});
|
||||
|
||||
Versions.recordVersionUpdate(typing);
|
||||
|
||||
return { log };
|
||||
|
||||
function writeOutputFile(filename: string, content: string) {
|
||||
fs.writeFileSync(path.join(outputPath, filename), content, 'utf-8');
|
||||
}
|
||||
}
|
||||
|
||||
function patchDefinitionFile(input: string): string {
|
||||
const pathToLibrary = /\/\/\/ <reference path="..\/(\w.+)\/.+"/gm;
|
||||
let output = input.replace(pathToLibrary, '/// <reference library="$1"');
|
||||
return output;
|
||||
}
|
||||
|
||||
function createMetadataJSON(typing: TypingsData): string {
|
||||
const clone: typeof typing = JSON.parse(JSON.stringify(typing));
|
||||
delete clone.root;
|
||||
return JSON.stringify(clone, undefined, 4);
|
||||
}
|
||||
|
||||
function createPackageJSON(typing: TypingsData, fileVersion: number): string {
|
||||
const dependencies: any = {};
|
||||
typing.moduleDependencies.forEach(d => dependencies[d] = '*');
|
||||
typing.libraryDependencies.forEach(d => dependencies[`@${settings.scopeName}/${d}`] = '*');
|
||||
|
||||
let version = `${typing.libraryMajorVersion}.${typing.libraryMinorVersion}.${fileVersion}`;
|
||||
if (settings.prereleaseTag) {
|
||||
version = `${version}-${settings.prereleaseTag}`;
|
||||
}
|
||||
|
||||
return JSON.stringify({
|
||||
name: `@${settings.scopeName}/${typing.typingsPackageName.toLowerCase()}`,
|
||||
version,
|
||||
description: `Type definitions for ${typing.libraryName} from ${typing.sourceRepoURL}`,
|
||||
main: '',
|
||||
scripts: {},
|
||||
author: typing.authors,
|
||||
license: 'MIT',
|
||||
typings: typing.definitionFilename,
|
||||
dependencies
|
||||
}, undefined, 4);
|
||||
}
|
||||
|
||||
function createReadme(typing: TypingsData) {
|
||||
const lines: string[] = [];
|
||||
|
||||
lines.push(`This package contains type definitions for ${typing.libraryName}.`)
|
||||
if (typing.projectName) {
|
||||
lines.push('');
|
||||
lines.push(`The project URL or description is ${typing.projectName}`);
|
||||
}
|
||||
|
||||
if (typing.authors) {
|
||||
lines.push('');
|
||||
lines.push(`These definitions were written by ${typing.authors}.`);
|
||||
}
|
||||
|
||||
lines.push('');
|
||||
lines.push(`Typings were exported from ${typing.sourceRepoURL} in the ${typing.typingsPackageName} directory.`);
|
||||
|
||||
lines.push('');
|
||||
lines.push(`Additional Details`)
|
||||
lines.push(` * Last updated: ${(new Date()).toUTCString()}`);
|
||||
lines.push(` * Typings kind: ${typing.kind}`);
|
||||
lines.push(` * Library Dependencies: ${typing.libraryDependencies.length ? typing.libraryDependencies.join(', ') : 'none'}`);
|
||||
lines.push(` * Module Dependencies: ${typing.moduleDependencies.length ? typing.moduleDependencies.join(', ') : 'none'}`);
|
||||
lines.push(` * Global values: ${typing.globals.length ? typing.globals.join(', ') : 'none'}`);
|
||||
lines.push('');
|
||||
|
||||
return lines.join('\r\n');
|
||||
}
|
||||
|
||||
namespace Versions {
|
||||
const versionFilename = 'versions.json';
|
||||
|
||||
interface VersionMap {
|
||||
[typingsPackageName: string]: {
|
||||
lastVersion: number;
|
||||
lastContentHash: string;
|
||||
};
|
||||
}
|
||||
|
||||
let _versionData: VersionMap = undefined;
|
||||
function loadVersions() {
|
||||
if(_versionData === undefined) {
|
||||
_versionData = fs.existsSync(versionFilename) ? JSON.parse(fs.readFileSync(versionFilename, 'utf-8')) : {};
|
||||
}
|
||||
return _versionData;
|
||||
}
|
||||
function saveVersions(data: VersionMap) {
|
||||
fs.writeFileSync(versionFilename, JSON.stringify(data, undefined, 4));
|
||||
}
|
||||
|
||||
export function recordVersionUpdate(typing: TypingsData) {
|
||||
const key = typing.typingsPackageName;
|
||||
const data = loadVersions();
|
||||
data[key] = { lastVersion: computeVersion(typing), lastContentHash: typing.contentHash };
|
||||
saveVersions(data);
|
||||
}
|
||||
|
||||
function getLastVersion(typing: TypingsData) {
|
||||
const key = typing.typingsPackageName;
|
||||
const data = loadVersions();
|
||||
const entry = data[key];
|
||||
return entry || { lastVersion: 0, lastContentHash: '' };
|
||||
}
|
||||
|
||||
export function computeVersion(typing: TypingsData): number {
|
||||
const forceUpdate = process.argv.some(arg => arg === '--forceUpdate');
|
||||
const lastVersion = getLastVersion(typing);
|
||||
const increment = (forceUpdate || (lastVersion.lastContentHash !== typing.contentHash)) ? 1 : 0;
|
||||
return lastVersion.lastVersion + increment;
|
||||
}
|
||||
}
|
||||
74
packages/types-publisher/src/lib/package-publisher.ts
Normal file
74
packages/types-publisher/src/lib/package-publisher.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { TypingsData, DefinitionFileKind, mkdir, getOutputPath, settings } from './common';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as child_process from 'child_process';
|
||||
import * as request from 'request';
|
||||
|
||||
interface NpmRegistryResult {
|
||||
versions: {
|
||||
[key: string]: {};
|
||||
}
|
||||
error: string;
|
||||
}
|
||||
|
||||
export function publishPackage(typing: TypingsData, done: (log: string[], errors: string[]) => void) {
|
||||
const log: string[] = [];
|
||||
const errors: string[] = [];
|
||||
|
||||
const outputPath = getOutputPath(typing);
|
||||
|
||||
log.push(`Possibly publishing ${typing.libraryName}`);
|
||||
|
||||
// Read package.json for version number we would be publishing
|
||||
const localVersion: string = JSON.parse(fs.readFileSync(path.join(outputPath, 'package.json'), 'utf-8')).version;
|
||||
log.push(`Local version from package.json is ${localVersion}`);
|
||||
|
||||
// Hit e.g. http://registry.npmjs.org/@ryancavanaugh%2fjquery for version data
|
||||
const registryUrl = `http://registry.npmjs.org/@${settings.scopeName}%2F${typing.typingsPackageName}`;
|
||||
log.push(`Fetch registry data from ${registryUrl}`);
|
||||
|
||||
// See if this version already exists
|
||||
request.get(registryUrl, (err: any, resp: any, bodyString: string) => {
|
||||
const body: NpmRegistryResult = JSON.parse(bodyString);
|
||||
|
||||
if (body.error === "Not found") {
|
||||
// OK, just haven't published this one before
|
||||
log.push('Registry indicates this is a new package');
|
||||
} else if (body.error) {
|
||||
// Critical failure
|
||||
log.push('Unexpected response, refer to error log');
|
||||
errors.push(`NPM registry failure for ${registryUrl}: Unexpected error content ${body.error})`);
|
||||
done(log, errors);
|
||||
return;
|
||||
} else {
|
||||
const remoteVersionExists = body.versions[localVersion] !== undefined;
|
||||
if (remoteVersionExists) {
|
||||
log.push(`Remote version already exists`);
|
||||
done(log, errors);
|
||||
return;
|
||||
} else {
|
||||
log.push(`Remote version does not exist`);
|
||||
}
|
||||
}
|
||||
|
||||
// Made it to here, so proceed with update
|
||||
const args: string[] = ['npm', 'publish', path.resolve(outputPath), '--access public'];
|
||||
if (settings.tag) {
|
||||
args.push(`--tag ${settings.tag}`);
|
||||
}
|
||||
|
||||
const cmd = args.join(' ');
|
||||
log.push(`Run ${cmd}`);
|
||||
try {
|
||||
const result = <string>child_process.execSync(cmd, { encoding: 'utf-8' });
|
||||
log.push(`Ran successfully`);
|
||||
log.push(result);
|
||||
} catch(e) {
|
||||
errors.push(`Publish failed: ${JSON.stringify(e)}`);
|
||||
log.push('Publish failed, refer to error log');
|
||||
}
|
||||
|
||||
done(log, errors);
|
||||
});
|
||||
}
|
||||
|
||||
99
packages/types-publisher/src/parse-definitions.ts
Normal file
99
packages/types-publisher/src/parse-definitions.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import * as parser from './lib/definition-parser';
|
||||
import { TypingsData, DefinitionFileKind, RejectionReason, TypingParseSucceedResult, TypingParseFailResult,
|
||||
settings, isSuccess, isFail, writeLogSync, writeDataFile, typesDataFilename } from './lib/common';
|
||||
|
||||
import fs = require('fs');
|
||||
import path = require('path');
|
||||
|
||||
function processDir(folderPath: string, name: string): { data: TypingsData, log: string[], warnings: string[], outcome: string } {
|
||||
let data: TypingsData;
|
||||
let outcome: string;
|
||||
|
||||
const info = parser.getTypingInfo(folderPath);
|
||||
const log = info.log;
|
||||
if (isSuccess(info)) {
|
||||
data = info.data;
|
||||
outcome = `Succeeded (${info.data.kind})`;
|
||||
|
||||
} else if (isFail(info)) {
|
||||
data = undefined;
|
||||
outcome = `Failed (${RejectionReason[info.rejectionReason]})`;
|
||||
}
|
||||
|
||||
return { data, log, warnings: info.warnings, outcome: outcome };
|
||||
}
|
||||
|
||||
function filterPaths(paths: string[]): { name: string; path: string; }[] {
|
||||
return paths
|
||||
// Remove hidden paths
|
||||
.filter(s => s.substr(0, 1) !== '_' && s.substr(0, 1) !== '.')
|
||||
// Sort by name
|
||||
.sort()
|
||||
// Combine paths
|
||||
.map(s => ({ name: s, path: path.join(settings.definitelyTypedPath, s) }))
|
||||
// Remove non-folders
|
||||
.filter(s => fs.statSync(s.path).isDirectory());
|
||||
}
|
||||
|
||||
function main() {
|
||||
const summaryLog: string[] = [];
|
||||
const detailedLog: string[] = [];
|
||||
|
||||
summaryLog.push('# Typing Publish Report Summary');
|
||||
summaryLog.push(`Started at ${(new Date()).toUTCString()}`);
|
||||
|
||||
// TypesData
|
||||
fs.readdir(settings.definitelyTypedPath, (err, paths) => {
|
||||
const folders = filterPaths(paths);
|
||||
|
||||
summaryLog.push(`Found ${folders.length} typings folders in ${settings.definitelyTypedPath}`);
|
||||
|
||||
const outcomes: { [name: string]: number} = {};
|
||||
const warningLog: string[] = [];
|
||||
const typings: { [name: string]: TypingsData } = {};
|
||||
|
||||
folders.forEach(s => {
|
||||
const result = processDir(s.path, s.name);
|
||||
|
||||
// Record outcome
|
||||
outcomes[result.outcome] = (outcomes[result.outcome] || 0) + 1;
|
||||
|
||||
detailedLog.push(`# ${s.name}`);
|
||||
|
||||
// Push warnings
|
||||
if (result.warnings.length > 0) {
|
||||
warningLog.push(` * ${s.name}`);
|
||||
result.warnings.forEach(w => {
|
||||
warningLog.push(` * ${w}`);
|
||||
detailedLog.push(`**Warning**: ${w}`);
|
||||
});
|
||||
}
|
||||
|
||||
if (result.data !== undefined) {
|
||||
typings[s.name] = result.data;
|
||||
}
|
||||
|
||||
// Flush detailed log
|
||||
result.log.forEach(e => detailedLog.push(e));
|
||||
});
|
||||
|
||||
summaryLog.push('\r\n### Overall Results\r\n');
|
||||
|
||||
summaryLog.push(' * Pass / fail');
|
||||
|
||||
const outcomeKeys = Object.keys(outcomes);
|
||||
outcomeKeys.sort();
|
||||
outcomeKeys.forEach(k => {
|
||||
summaryLog.push(` * ${k}: ${outcomes[k]}`);
|
||||
});
|
||||
|
||||
summaryLog.push('\r\n### Warnings\r\n');
|
||||
warningLog.forEach(w => summaryLog.push(w));
|
||||
|
||||
writeLogSync('parser-log-summary.md', summaryLog);
|
||||
writeLogSync('parser-log-details.md', detailedLog);
|
||||
writeDataFile(typesDataFilename, typings);
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
40
packages/types-publisher/src/publish-packages.ts
Normal file
40
packages/types-publisher/src/publish-packages.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import * as common from './lib/common';
|
||||
import * as publisher from './lib/package-publisher';
|
||||
|
||||
const typeData = <common.TypesDataFile>common.readDataFile(common.typesDataFilename);
|
||||
|
||||
if (typeData === undefined) {
|
||||
console.log('Run parse-definitions and generate-packages first!');
|
||||
} else {
|
||||
main();
|
||||
}
|
||||
|
||||
function main() {
|
||||
const log: string[] = [];
|
||||
const publishQueue = Object.keys(typeData);
|
||||
next();
|
||||
|
||||
function next() {
|
||||
common.writeLogSync('publishing.md', log);
|
||||
if (publishQueue.length === 0) {
|
||||
console.log('Done!');
|
||||
return;
|
||||
}
|
||||
|
||||
const packageName = publishQueue.shift();
|
||||
console.log(`Publishing ${packageName}...`);
|
||||
|
||||
const typing = typeData[packageName];
|
||||
publisher.publishPackage(typing, (publishLog, errors) => {
|
||||
log.push(` * ${packageName}`);
|
||||
publishLog.forEach(line => log.push(` * ${line}`));
|
||||
|
||||
errors.forEach(err => {
|
||||
log.push(` * ERROR: ${err}`);
|
||||
console.log(` Error! ${err}`);
|
||||
});
|
||||
|
||||
next();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
import * as parser from './definition-parser';
|
||||
import * as publisher from './definition-publisher';
|
||||
|
||||
import fs = require('fs');
|
||||
import path = require('path');
|
||||
|
||||
const settings: PublishSettings = JSON.parse(fs.readFileSync('./settings.json', 'utf-8'));
|
||||
|
||||
const versionFile = 'versions.json';
|
||||
|
||||
|
||||
const summaryLog: string[] = [];
|
||||
const detailedLog: string[] = [];
|
||||
const outcomes: { [s: string]: number } = {};
|
||||
const kinds: { [s: string]: number } = {};
|
||||
|
||||
function recordKind(s: string) {
|
||||
kinds[s] = (kinds[s] || 0) + 1;
|
||||
}
|
||||
|
||||
function recordOutcome(s: string) {
|
||||
outcomes[s] = (outcomes[s] || 0) + 1;
|
||||
}
|
||||
|
||||
function processDir(folderPath: string, name: string) {
|
||||
detailedLog.push(`## ${name}`);
|
||||
|
||||
const info = parser.getTypingInfo(folderPath);
|
||||
|
||||
if (parser.isSuccess(info)) {
|
||||
detailedLog.push('### File Parse Succeeded');
|
||||
detailedLog.push(`Detected a ${info.data.kind} typing definition.`);
|
||||
detailedLog.push('```js');
|
||||
detailedLog.push(JSON.stringify(info.data, undefined, 4));
|
||||
detailedLog.push('```');
|
||||
recordOutcome(`Succeeded (${info.data.kind})`);
|
||||
recordKind(info.data.kind);
|
||||
|
||||
detailedLog.push('### Publish');
|
||||
const publishLog = publisher.publish(info.data);
|
||||
for(const line of publishLog.log) {
|
||||
detailedLog.push(` > ${line}\r\n\r\n`);
|
||||
}
|
||||
} else if(parser.isFail(info)) {
|
||||
detailedLog.push('### File Parse Failed');
|
||||
switch (info.rejectionReason) {
|
||||
case parser.RejectionReason.BadFileFormat:
|
||||
recordOutcome('Failed: Bad file format');
|
||||
detailedLog.push('Bad file format');
|
||||
break;
|
||||
case parser.RejectionReason.ReferencePaths:
|
||||
recordOutcome('Failed: Reference paths not allowed');
|
||||
detailedLog.push('Reference paths are not allowed (use library references instead)');
|
||||
break;
|
||||
case parser.RejectionReason.TooManyFiles:
|
||||
recordOutcome('Failed: Too many files');
|
||||
detailedLog.push('Failed: Only one .d.ts file per folder is currently supported');
|
||||
break;
|
||||
default:
|
||||
recordOutcome('??');
|
||||
}
|
||||
}
|
||||
|
||||
detailedLog.push('### Parser Log');
|
||||
for(const line of info.log) detailedLog.push('> ' + line + '\r\n');
|
||||
detailedLog.push('');
|
||||
}
|
||||
|
||||
function main() {
|
||||
summaryLog.push('# Typing Publish Report Summary');
|
||||
summaryLog.push(`Started at ${(new Date()).toUTCString()}`);
|
||||
|
||||
try {
|
||||
fs.mkdirSync(settings.outputPath);
|
||||
} catch(e) { }
|
||||
|
||||
fs.readdir(settings.definitelyTypedPath, (err, paths) => {
|
||||
const folders = paths
|
||||
// Remove hidden paths
|
||||
.filter(s => s.substr(0, 1) !== '_' && s.substr(0, 1) !== '.')
|
||||
// Combine paths
|
||||
.map(s => ({ name: s, path: path.join(settings.definitelyTypedPath, s) }))
|
||||
// Remove non-folders
|
||||
.filter(s => fs.statSync(s.path).isDirectory());
|
||||
|
||||
folders.sort();
|
||||
console.log(`Found ${folders.length} typings folders.`);
|
||||
|
||||
folders.forEach(s => processDir(s.path, s.name));
|
||||
|
||||
summaryLog.push('\r\n### Overall Results\r\n');
|
||||
|
||||
summaryLog.push(' * Pass / fail');
|
||||
const outcomeKeys = Object.keys(outcomes);
|
||||
outcomeKeys.sort();
|
||||
outcomeKeys.forEach(k => {
|
||||
summaryLog.push(` * ${k}: ${outcomes[k]}`);
|
||||
});
|
||||
|
||||
summaryLog.push(' * Typing Kind');
|
||||
const typingKeys = Object.keys(kinds);
|
||||
typingKeys.sort();
|
||||
typingKeys.forEach(k => {
|
||||
summaryLog.push(` * ${k}: ${kinds[k]}`);
|
||||
});
|
||||
|
||||
const logmd = summaryLog.join('\r\n') + '\r\n\r\n# Detailed Report\r\n\r\n' + detailedLog.join('\r\n');
|
||||
fs.writeFile('log.md', logmd, 'utf-8');
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
16
packages/types-publisher/src/searchRecord.d.ts
vendored
Normal file
16
packages/types-publisher/src/searchRecord.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
interface SearchRecord {
|
||||
// types package name
|
||||
typePackageName: string;
|
||||
// npm package name
|
||||
npmPackageName: string;
|
||||
// globals
|
||||
globals: string[];
|
||||
// modules
|
||||
declaredExternalModules: string[];
|
||||
// project name
|
||||
packageName: string;
|
||||
// library name
|
||||
libraryName: string;
|
||||
// downloads in the last month from NPM
|
||||
downloads?: number;
|
||||
}
|
||||
@@ -1,10 +1,17 @@
|
||||
{
|
||||
"files": [
|
||||
"src/run.ts",
|
||||
"src/definition-publisher.ts",
|
||||
"src/definition-parser.ts",
|
||||
"src/settings.d.ts",
|
||||
"typings/tsd.d.ts"
|
||||
"src/searchRecord.d.ts",
|
||||
"typings/tsd.d.ts",
|
||||
|
||||
"src/lib/definition-parser.ts",
|
||||
"src/lib/package-generator.ts",
|
||||
"src/lib/package-publisher.ts",
|
||||
"src/lib/common.ts",
|
||||
|
||||
"src/parse-definitions.ts",
|
||||
"src/generate-packages.ts",
|
||||
"src/publish-packages.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
|
||||
16
packages/types-publisher/typings/form-data/form-data.d.ts
vendored
Normal file
16
packages/types-publisher/typings/form-data/form-data.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Type definitions for form-data
|
||||
// Project: https://github.com/felixge/node-form-data
|
||||
// Definitions by: Carlos Ballesteros Velasco <https://github.com/soywiz>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
|
||||
// Imported from: https://github.com/soywiz/typescript-node-definitions/form-data.d.ts
|
||||
|
||||
declare module "form-data" {
|
||||
export class FormData {
|
||||
append(key: string, value: any, options?: any): FormData;
|
||||
getHeaders(): Object;
|
||||
// TODO expand pipe
|
||||
pipe(to: any): any;
|
||||
submit(params: string|Object, callback: (error: any, response: any) => void): any;
|
||||
}
|
||||
}
|
||||
262
packages/types-publisher/typings/request/request.d.ts
vendored
Normal file
262
packages/types-publisher/typings/request/request.d.ts
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
// Type definitions for request
|
||||
// Project: https://github.com/mikeal/request
|
||||
// Definitions by: Carlos Ballesteros Velasco <https://github.com/soywiz>, bonnici <https://github.com/bonnici>, Bart van der Schoor <https://github.com/Bartvds>, Joe Skeen <http://github.com/joeskeen>, Christopher Currens <https://github.com/ccurrens>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
|
||||
// Imported from: https://github.com/soywiz/typescript-node-definitions/d.ts
|
||||
|
||||
/// <reference path="../node/node.d.ts" />
|
||||
/// <reference path="../form-data/form-data.d.ts" />
|
||||
|
||||
declare module 'request' {
|
||||
import stream = require('stream');
|
||||
import http = require('http');
|
||||
import https = require('https');
|
||||
import FormData = require('form-data');
|
||||
import url = require('url');
|
||||
import fs = require('fs');
|
||||
|
||||
namespace request {
|
||||
export interface RequestAPI<TRequest extends Request,
|
||||
TOptions extends CoreOptions,
|
||||
TUriUrlOptions> {
|
||||
|
||||
defaults(options: TOptions): RequestAPI<TRequest, TOptions, RequiredUriUrl>;
|
||||
defaults(options: RequiredUriUrl & TOptions): DefaultUriUrlRequestApi<TRequest, TOptions, OptionalUriUrl>;
|
||||
|
||||
(uri: string, options?: TOptions, callback?: RequestCallback): TRequest;
|
||||
(uri: string, callback?: RequestCallback): TRequest;
|
||||
(options: TUriUrlOptions & TOptions, callback?: RequestCallback): TRequest;
|
||||
|
||||
get(uri: string, options?: TOptions, callback?: RequestCallback): TRequest;
|
||||
get(uri: string, callback?: RequestCallback): TRequest;
|
||||
get(options: TUriUrlOptions & TOptions, callback?: RequestCallback): TRequest;
|
||||
|
||||
post(uri: string, options?: TOptions, callback?: RequestCallback): TRequest;
|
||||
post(uri: string, callback?: RequestCallback): TRequest;
|
||||
post(options: TUriUrlOptions & TOptions, callback?: RequestCallback): TRequest;
|
||||
|
||||
put(uri: string, options?: TOptions, callback?: RequestCallback): TRequest;
|
||||
put(uri: string, callback?: RequestCallback): TRequest;
|
||||
put(options: TUriUrlOptions & TOptions, callback?: RequestCallback): TRequest;
|
||||
|
||||
head(uri: string, options?: TOptions, callback?: RequestCallback): TRequest;
|
||||
head(uri: string, callback?: RequestCallback): TRequest;
|
||||
head(options: TUriUrlOptions & TOptions, callback?: RequestCallback): TRequest;
|
||||
|
||||
patch(uri: string, options?: TOptions, callback?: RequestCallback): TRequest;
|
||||
patch(uri: string, callback?: RequestCallback): TRequest;
|
||||
patch(options: TUriUrlOptions & TOptions, callback?: RequestCallback): TRequest;
|
||||
|
||||
del(uri: string, options?: TOptions, callback?: RequestCallback): TRequest;
|
||||
del(uri: string, callback?: RequestCallback): TRequest;
|
||||
del(options: TUriUrlOptions & TOptions, callback?: RequestCallback): TRequest;
|
||||
|
||||
forever(agentOptions: any, optionsArg: any): TRequest;
|
||||
jar(): CookieJar;
|
||||
cookie(str: string): Cookie;
|
||||
|
||||
initParams: any;
|
||||
debug: boolean;
|
||||
}
|
||||
|
||||
interface DefaultUriUrlRequestApi<TRequest extends Request,
|
||||
TOptions extends CoreOptions,
|
||||
TUriUrlOptions> extends RequestAPI<TRequest, TOptions, TUriUrlOptions> {
|
||||
|
||||
defaults(options: TOptions): DefaultUriUrlRequestApi<TRequest, TOptions, OptionalUriUrl>;
|
||||
(): TRequest;
|
||||
get(): TRequest;
|
||||
post(): TRequest;
|
||||
put(): TRequest;
|
||||
head(): TRequest;
|
||||
patch(): TRequest;
|
||||
del(): TRequest;
|
||||
}
|
||||
|
||||
interface CoreOptions {
|
||||
baseUrl?: string;
|
||||
callback?: (error: any, response: http.IncomingMessage, body: any) => void;
|
||||
jar?: any; // CookieJar
|
||||
formData?: any; // Object
|
||||
form?: any; // Object or string
|
||||
auth?: AuthOptions;
|
||||
oauth?: OAuthOptions;
|
||||
aws?: AWSOptions;
|
||||
hawk?: HawkOptions;
|
||||
qs?: any;
|
||||
json?: any;
|
||||
multipart?: RequestPart[] | Multipart;
|
||||
agent?: http.Agent | https.Agent;
|
||||
agentOptions?: any;
|
||||
agentClass?: any;
|
||||
forever?: any;
|
||||
host?: string;
|
||||
port?: number;
|
||||
method?: string;
|
||||
headers?: Headers;
|
||||
body?: any;
|
||||
followRedirect?: boolean | ((response: http.IncomingMessage) => boolean);
|
||||
followAllRedirects?: boolean;
|
||||
maxRedirects?: number;
|
||||
encoding?: string;
|
||||
pool?: any;
|
||||
timeout?: number;
|
||||
proxy?: any;
|
||||
strictSSL?: boolean;
|
||||
gzip?: boolean;
|
||||
preambleCRLF?: boolean;
|
||||
postambleCRLF?: boolean;
|
||||
key?: Buffer;
|
||||
cert?: Buffer;
|
||||
passphrase?: string;
|
||||
ca?: Buffer;
|
||||
har?: HttpArchiveRequest;
|
||||
useQuerystring?: boolean;
|
||||
}
|
||||
|
||||
interface UriOptions {
|
||||
uri: string;
|
||||
}
|
||||
interface UrlOptions {
|
||||
url: string;
|
||||
}
|
||||
export type RequiredUriUrl = UriOptions | UrlOptions;
|
||||
|
||||
interface OptionalUriUrl {
|
||||
uri?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export type OptionsWithUri = UriOptions & CoreOptions;
|
||||
export type OptionsWithUrl = UrlOptions & CoreOptions;
|
||||
export type Options = OptionsWithUri | OptionsWithUrl;
|
||||
|
||||
export interface RequestCallback {
|
||||
(error: any, response: http.IncomingMessage, body: any): void;
|
||||
}
|
||||
|
||||
export interface HttpArchiveRequest {
|
||||
url?: string;
|
||||
method?: string;
|
||||
headers?: NameValuePair[];
|
||||
postData?: {
|
||||
mimeType?: string;
|
||||
params?: NameValuePair[];
|
||||
}
|
||||
}
|
||||
|
||||
export interface NameValuePair {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface Multipart {
|
||||
chunked?: boolean;
|
||||
data?: {
|
||||
'content-type'?: string,
|
||||
body: string
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface RequestPart {
|
||||
headers?: Headers;
|
||||
body: any;
|
||||
}
|
||||
|
||||
export interface Request extends stream.Stream {
|
||||
readable: boolean;
|
||||
writable: boolean;
|
||||
|
||||
getAgent(): http.Agent;
|
||||
//start(): void;
|
||||
//abort(): void;
|
||||
pipeDest(dest: any): void;
|
||||
setHeader(name: string, value: string, clobber?: boolean): Request;
|
||||
setHeaders(headers: Headers): Request;
|
||||
qs(q: Object, clobber?: boolean): Request;
|
||||
form(): FormData.FormData;
|
||||
form(form: any): Request;
|
||||
multipart(multipart: RequestPart[]): Request;
|
||||
json(val: any): Request;
|
||||
aws(opts: AWSOptions, now?: boolean): Request;
|
||||
auth(username: string, password: string, sendInmediately?: boolean, bearer?: string): Request;
|
||||
oauth(oauth: OAuthOptions): Request;
|
||||
jar(jar: CookieJar): Request;
|
||||
|
||||
on(event: string, listener: Function): this;
|
||||
on(event: 'request', listener: (req: http.ClientRequest) => void): this;
|
||||
on(event: 'response', listener: (resp: http.IncomingMessage) => void): this;
|
||||
on(event: 'data', listener: (data: Buffer | string) => void): this;
|
||||
on(event: 'error', listener: (e: Error) => void): this;
|
||||
on(event: 'complete', listener: (resp: http.IncomingMessage, body?: string | Buffer) => void): this;
|
||||
|
||||
write(buffer: Buffer, cb?: Function): boolean;
|
||||
write(str: string, cb?: Function): boolean;
|
||||
write(str: string, encoding: string, cb?: Function): boolean;
|
||||
write(str: string, encoding?: string, fd?: string): boolean;
|
||||
end(): void;
|
||||
end(chunk: Buffer, cb?: Function): void;
|
||||
end(chunk: string, cb?: Function): void;
|
||||
end(chunk: string, encoding: string, cb?: Function): void;
|
||||
pause(): void;
|
||||
resume(): void;
|
||||
abort(): void;
|
||||
destroy(): void;
|
||||
toJSON(): Object;
|
||||
}
|
||||
|
||||
export interface Headers {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface AuthOptions {
|
||||
user?: string;
|
||||
username?: string;
|
||||
pass?: string;
|
||||
password?: string;
|
||||
sendImmediately?: boolean;
|
||||
bearer?: string;
|
||||
}
|
||||
|
||||
export interface OAuthOptions {
|
||||
callback?: string;
|
||||
consumer_key?: string;
|
||||
consumer_secret?: string;
|
||||
token?: string;
|
||||
token_secret?: string;
|
||||
verifier?: string;
|
||||
}
|
||||
|
||||
export interface HawkOptions {
|
||||
credentials: any;
|
||||
}
|
||||
|
||||
export interface AWSOptions {
|
||||
secret: string;
|
||||
bucket?: string;
|
||||
}
|
||||
|
||||
export interface CookieJar {
|
||||
setCookie(cookie: Cookie, uri: string | url.Url, options?: any): void
|
||||
getCookieString(uri: string | url.Url): string
|
||||
getCookies(uri: string | url.Url): Cookie[]
|
||||
}
|
||||
|
||||
export interface CookieValue {
|
||||
name: string;
|
||||
value: any;
|
||||
httpOnly: boolean;
|
||||
}
|
||||
|
||||
export interface Cookie extends Array<CookieValue> {
|
||||
constructor(name: string, req: Request): void;
|
||||
str: string;
|
||||
expires: Date;
|
||||
path: string;
|
||||
toString(): string;
|
||||
}
|
||||
}
|
||||
var request: request.RequestAPI<request.Request, request.CoreOptions, request.RequiredUriUrl>;
|
||||
export = request;
|
||||
}
|
||||
1
packages/types-publisher/typings/tsd.d.ts
vendored
1
packages/types-publisher/typings/tsd.d.ts
vendored
@@ -1,3 +1,4 @@
|
||||
|
||||
/// <reference path="node/node.d.ts" />
|
||||
/// <reference path="typescript/typescript.d.ts" />
|
||||
/// <reference path="request/request.d.ts" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user