Skip to content

Commit

Permalink
Merge pull request #10 from Polymer/pr-multi-file
Browse files Browse the repository at this point in the history
Create a typings file per source file, instead of per package.
  • Loading branch information
aomarks authored Oct 20, 2017
2 parents b61427b + a40bd62 commit 1e6054c
Show file tree
Hide file tree
Showing 38 changed files with 1,504 additions and 1,346 deletions.
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
node_modules
lib
src/test/fixtures
/node_modules
/lib
/src/test/fixtures
59 changes: 50 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,23 @@
},
"dependencies": {
"@types/doctrine": "0.0.3",
"@types/fs-extra": "^4.0.2",
"command-line-args": "^4.0.7",
"command-line-usage": "^4.0.1",
"doctrine": "^2.0.0",
"escodegen": "^1.9.0",
"fs-extra": "^4.0.2",
"polymer-analyzer": "^2.3.0"
},
"devDependencies": {
"@types/chai": "^4.0.4",
"@types/glob": "^5.0.33",
"@types/mocha": "^2.2.43",
"bower": "^1.8.2",
"chai": "^4.1.2",
"clang-format": "^1.0.55",
"glob": "^7.1.2",
"mocha": "^3.5.3",
"rimraf": "^2.6.2",
"source-map-support": "^0.5.0",
"typescript": "^2.5.3",
"watchy": "^0.7.0"
Expand Down
19 changes: 11 additions & 8 deletions scripts/make-goldens.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,23 @@
*/

const fs = require('fs');
const fsExtra = require('fs-extra');
const path = require('path');
const rimraf = require('rimraf');

const {generateDeclarations} = require('../lib/gen-ts');

const fixturesDir = path.join(__dirname, '..', 'src', 'test', 'fixtures');
const goldensDir = path.join(__dirname, '..', 'src', 'test', 'goldens');

rimraf.sync(goldensDir);
fs.mkdirSync(goldensDir);
fsExtra.emptyDir(goldensDir);

for (const dir of fs.readdirSync(fixturesDir)) {
generateDeclarations(path.join(fixturesDir, dir)).then((declarations) => {
const outDir = path.join(goldensDir, dir);
fs.mkdirSync(outDir);
fs.writeFileSync(path.join(outDir, 'expected.d.ts'), declarations);
for (const fixture of fs.readdirSync(fixturesDir)) {
console.log('making goldens for ' + fixture);
generateDeclarations(path.join(fixturesDir, fixture)).then((declarations) => {
for (const [filename, contents] of declarations) {
const outPath = path.join(goldensDir, fixture, filename);
fsExtra.mkdirsSync(path.dirname(outPath));
fs.writeFileSync(outPath, contents);
};
});
}
28 changes: 21 additions & 7 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
* additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/

import * as fs from 'fs';
import * as fsExtra from 'fs-extra';
import * as path from 'path';
import {generateDeclarations} from './gen-ts';

const commandLineArgs = require('command-line-args') as any;
Expand All @@ -33,9 +34,10 @@ const argDefs = [
description: 'Root directory of the package to analyze (default ".").',
},
{
name: 'out',
name: 'outDir',
type: String,
description: 'Type declarations output file path (default stdout).',
description:
'Type declarations output directory (default concatenated to stdout).',
},
];

Expand All @@ -61,15 +63,27 @@ async function run(argv: string[]) {
return;
}

const declarations = await generateDeclarations(args.root);
const fileMap = await generateDeclarations(args.root);

if (args.out) {
fs.writeFileSync(args.out, declarations);
if (args.outDir) {
console.log('Writing type declarations to ' + path.resolve(args.outDir));
await writeFileMap(args.outDir, fileMap);
} else {
process.stdout.write(declarations);
const concatenated = [...fileMap.values()].join('\n');
process.stdout.write(concatenated);
}
}

async function writeFileMap(
rootDir: string, files: Map<string, string>): Promise<void> {
const promises = [];
for (const [relPath, contents] of files) {
const fullPath = path.join(rootDir, relPath);
promises.push(fsExtra.outputFile(fullPath, contents));
}
await Promise.all(promises);
}

(async () => {
try {
await run(process.argv);
Expand Down
65 changes: 50 additions & 15 deletions src/gen-ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* rights grant found at http://polymer.github.io/PATENTS.txt
*/

import * as path from 'path';
import * as analyzer from 'polymer-analyzer';
import {Function as AnalyzerFunction} from 'polymer-analyzer/lib/javascript/function';

Expand All @@ -18,37 +19,71 @@ import {serializeTsDeclarations} from './ts-serialize';

/**
* Analyze all files in the given directory using Polymer Analyzer, and return
* a TypeScript declarations document string that describes its contents.
* TypeScript declaration document strings in a map keyed by relative path.
*/
export async function generateDeclarations(rootDir: string): Promise<string> {
export async function generateDeclarations(rootDir: string):
Promise<Map<string, string>> {
const a = new analyzer.Analyzer({
urlLoader: new analyzer.FSUrlLoader(rootDir),
urlResolver: new analyzer.PackageUrlResolver(),
});
const analysis = await a.analyzePackage();
const ast = analyzerToAst(analysis);
return serializeTsDeclarations(ast);
const outFiles = new Map<string, string>();
for (const tsDoc of analyzerToAst(analysis)) {
outFiles.set(tsDoc.path, serializeTsDeclarations(tsDoc));
}
return outFiles;
}

/**
* Make a TypeScript declarations document from the given Polymer Analyzer
* Make TypeScript declaration documents from the given Polymer Analyzer
* result.
*/
function analyzerToAst(analysis: analyzer.Analysis): ts.Document {
const root: ts.Document = {
kind: 'document',
members: [],
};

for (const doc of analysis.getFeatures({kind: 'js-document'})) {
function analyzerToAst(analysis: analyzer.Analysis): ts.Document[] {
// Analyzer can produce multiple JS documents with the same URL (e.g. an HTML
// file with multiple inline scripts). We also might have multiple files with
// the same basename (e.g. `foo.html` with an inline script, and `foo.js`).
// We want to produce one declarations file for each basename, so we first
// group Analyzer documents by their declarations filename.
const declarationDocs = new Map<string, analyzer.Document[]>();
for (const jsDoc of analysis.getFeatures({kind: 'js-document'})) {
// TODO This is a very crude exclusion rule.
if (doc.url.match(/(test|demo)\//)) {
if (jsDoc.url.match(/(test|demo)\//)) {
continue;
}
handleDocument(doc, root);
const filename = makeDeclarationsFilename(jsDoc.url);
let docs = declarationDocs.get(filename);
if (!docs) {
docs = [];
declarationDocs.set(filename, docs);
}
docs.push(jsDoc);
}

const tsDocs = new Array<ts.Document>();
for (const [declarationsFilename, analyzerDocs] of declarationDocs) {
const tsDoc: ts.Document = {
kind: 'document',
path: declarationsFilename,
members: [],
};
for (const analyzerDoc of analyzerDocs) {
handleDocument(analyzerDoc, tsDoc);
}
if (tsDoc.members.length) {
tsDocs.push(tsDoc);
}
}
return tsDocs;
}

return root;
/**
* Create a TypeScript declarations filename for the given source document URL.
* Simply replaces the file extension with `d.ts`.
*/
function makeDeclarationsFilename(sourceUrl: string): string {
const parsed = path.parse(sourceUrl);
return path.join(parsed.dir, parsed.name) + '.d.ts';
}

/**
Expand Down
20 changes: 15 additions & 5 deletions src/test/gen-ts_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import {assert} from 'chai';
import * as fs from 'fs';
import * as glob from 'glob';
import * as path from 'path';

import {generateDeclarations} from '../gen-ts';
Expand All @@ -21,11 +22,20 @@ const goldens = path.join(__dirname, '..', '..', 'src', 'test', 'goldens');
suite('generateDeclarations', () => {
for (const fixture of fs.readdirSync(goldens)) {
test(fixture, async () => {
const golden =
fs.readFileSync(path.join(goldens, fixture, 'expected.d.ts'));
const declarations =
await generateDeclarations(path.join(fixtures, fixture));
assert.equal(declarations, golden.toString());
const actual = await generateDeclarations(path.join(fixtures, fixture));
const golden = readDirAsMap(path.join(goldens, fixture));
(assert as any).hasAllKeys(actual, [...golden.keys()]);
for (const filename of actual.keys()) {
assert.equal(actual.get(filename), golden.get(filename), filename);
}
}).timeout(30000); // These tests can take a long time.
}
});

function readDirAsMap(dir: string): Map<string, string> {
const files = new Map<string, string>();
for (const filename of glob.sync('**', {cwd: dir, nodir: true})) {
files.set(filename, fs.readFileSync(path.join(dir, filename)).toString());
}
return files;
}
27 changes: 27 additions & 0 deletions src/test/goldens/paper-behaviors/paper-button-behavior.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
declare namespace Polymer {

interface PaperButtonBehavior {

/**
* The z-depth of this element, from 0-5. Setting to 0 will remove the
* shadow, and each increasing number greater than 0 will be "deeper"
* than the last.
*/
elevation: number;
hostAttributes: Object|null;

/**
* In addition to `IronButtonState` behavior, when space key goes down,
* create a ripple down effect.
*/
_spaceKeyDownHandler(event: KeyboardEvent): any;

/**
* In addition to `IronButtonState` behavior, when space key goes up,
* create a ripple up effect.
*/
_spaceKeyUpHandler(event: KeyboardEvent): any;
_calculateElevation(): any;
_computeKeyboardClass(receivedFocusFromKeyboard: any): any;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
declare namespace Polymer {

/**
* Use `Polymer.PaperCheckedElementBehavior` to implement a custom element
* that has a `checked` property similar to `Polymer.IronCheckedElementBehavior`
* and is compatible with having a ripple effect.
*/
interface PaperCheckedElementBehavior {

/**
* Synchronizes the element's `active` and `checked` state.
*/
_buttonStateChanged(): any;

/**
* Synchronizes the element's checked state with its ripple effect.
*/
_checkedChanged(): any;
}
}
Loading

0 comments on commit 1e6054c

Please sign in to comment.