UNPKG

5.72 kBPlain TextView Raw
1/**
2 * @license
3 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. This
4 * code may only be used under the BSD style license found at
5 * http://polymer.github.io/LICENSE.txt The complete set of authors may be
6 * found at http://polymer.github.io/AUTHORS.txt The complete set of
7 * contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt Code
8 * distributed by Google as part of the polymer project is also subject to an
9 * additional IP rights grant found at http://polymer.github.io/PATENTS.txt
10 */
11
12import 'source-map-support/register';
13
14import * as fsExtra from 'fs-extra';
15import * as glob from 'glob';
16import * as path from 'path';
17
18import {Config, generateDeclarations} from './gen-ts';
19import {verifyTypings} from './verify';
20
21import commandLineArgs = require('command-line-args');
22import commandLineUsage = require('command-line-usage');
23
24const argDefs = [
25 {
26 name: 'help',
27 type: Boolean,
28 description: 'Print this help text.',
29 },
30 {
31 name: 'version',
32 type: Boolean,
33 description: 'Print the installed version.',
34 },
35 {
36 name: 'root',
37 type: String,
38 defaultValue: '.',
39 description: 'Root directory of the package to analyze (default ".").',
40 },
41 {
42 name: 'config',
43 type: String,
44 description:
45 'JSON configuration file (default "<root>/gen-tsd.json" if exists).',
46 },
47 {
48 name: 'outDir',
49 type: String,
50 description: 'Type declarations output directory (required).',
51 },
52 {
53 name: 'deleteExisting',
54 type: Boolean,
55 description: 'Recursively delete all .d.ts files in <outDir> before ' +
56 'writing new typings, excluding node_modules/, bower_components/, ' +
57 'or any file added using the <addReferences> or <autoImport> config ' +
58 'options.',
59 },
60 {
61 name: 'verify',
62 type: Boolean,
63 description: 'Compile the generated types with TypeScript 3.0 and fail ' +
64 'if they are invalid.',
65 },
66 {
67 name: 'googModules',
68 type: Boolean,
69 description: 'If true, outputs declarations in \'goog:\' modules instead' +
70 ' of using simple ES modules. This is a temporary hack to account for' +
71 ' how modules are resolved for TypeScript inside google3. This' +
72 ' is probably not at all useful for anyone but the Polymer team.'
73 }
74];
75
76interface Args {
77 help?: boolean;
78 version?: boolean;
79 root: string;
80 config?: string;
81 outDir?: string;
82 deleteExisting?: boolean;
83 verify?: boolean;
84 googModules?: boolean;
85}
86
87async function run(argv: string[]) {
88 const args = commandLineArgs(argDefs, {argv}) as Args;
89
90 if (args.help) {
91 console.log(commandLineUsage([
92 {
93 header: `gen-typescript-declarations`,
94 content: 'https://github.com/Polymer/tools/tree/master/packages/' +
95 'gen-typescript-declarations',
96 },
97 {
98 header: `Options`,
99 optionList: argDefs,
100 }
101 ]));
102 return;
103 }
104
105 if (args.version) {
106 console.log(require('../package.json').version);
107 return;
108 }
109
110 if (!args.outDir) {
111 throw new Error('--outDir is required');
112 }
113 const outDir = path.resolve(args.outDir);
114
115 if (!args.config) {
116 const p = path.join(args.root, 'gen-tsd.json');
117 if (await fsExtra.pathExists(p)) {
118 args.config = p;
119 }
120 }
121 let config: Config = {};
122 if (args.config) {
123 console.info(`Loading config from "${args.config}".`);
124 config = JSON.parse(await fsExtra.readFile(args.config, 'utf8')) as Config;
125 }
126 if (args.googModules) {
127 config.googModules = true;
128 }
129
130 const fileMap = await generateDeclarations(args.root, config);
131
132 if (args.deleteExisting) {
133 let dtsFiles = glob.sync('**/*.d.ts', {
134 cwd: outDir,
135 absolute: true,
136 nodir: true,
137 ignore: [
138 'node_modules/**',
139 'bower_components/**',
140 ]
141 });
142
143 // If the addReferences or autoImport options are used, it's probably to add
144 // some manually written typings. Since manually written typing files won't
145 // get re-generated, we shouldn't delete them.
146 const dontDelete = new Set<string>();
147 for (const dtsFilePaths of Object.values(config.addReferences || {})) {
148 for (const dtsFilePath of dtsFilePaths) {
149 dontDelete.add(path.resolve(args.root, dtsFilePath));
150 }
151 }
152 for (const localModuleSpecifier of Object.keys(config.autoImport || {})) {
153 const dtsFilePath = localModuleSpecifier.replace(/\.js$/, '') + '.d.ts';
154 dontDelete.add(path.resolve(args.root, dtsFilePath));
155 }
156 dtsFiles = dtsFiles.filter((filepath) => !dontDelete.has(filepath));
157
158 console.log(
159 `Deleting ${dtsFiles.length} existing d.ts files from ${outDir}`);
160 await Promise.all(dtsFiles.map((filepath) => fsExtra.remove(filepath)));
161 }
162
163 console.log(`Writing type declarations to ${outDir}`);
164 await writeFileMap(args.outDir, fileMap);
165
166 if (args.verify) {
167 console.log('Verifying type declarations');
168 const declarationFiles =
169 [...fileMap.keys()].map((filePath) => path.join(outDir, filePath));
170 const result = verifyTypings(declarationFiles);
171 if (result.success === true) {
172 console.log('Compilation successful');
173 } else {
174 console.log('Compilation failed');
175 console.log(result.errorLog);
176 process.exit(1);
177 }
178 }
179}
180
181async function writeFileMap(
182 rootDir: string, files: Map<string, string>): Promise<void> {
183 const promises = [];
184 for (const [relPath, contents] of files) {
185 const fullPath = path.join(rootDir, relPath);
186 promises.push(fsExtra.outputFile(fullPath, contents));
187 }
188 await Promise.all(promises);
189}
190
191(async () => {
192 try {
193 await run(process.argv);
194 } catch (e) {
195 console.error(e);
196 process.exit(1);
197 }
198})();