1 | (function (factory) {
|
2 | if (typeof module === "object" && typeof module.exports === "object") {
|
3 | var v = factory(require, exports);
|
4 | if (v !== undefined) module.exports = v;
|
5 | }
|
6 | else if (typeof define === "function" && define.amd) {
|
7 | define(["require", "exports", "fs", "glob", "mkdirp", "os", "path", "typescript"], factory);
|
8 | }
|
9 | })(function (require, exports) {
|
10 | "use strict";
|
11 | Object.defineProperty(exports, "__esModule", { value: true });
|
12 | const fs = require("fs");
|
13 | const glob = require("glob");
|
14 | const mkdirp = require("mkdirp");
|
15 | const os = require("os");
|
16 | const pathUtil = require("path");
|
17 | const ts = require("typescript");
|
18 |
|
19 | const DTSLEN = '.d.ts'.length;
|
20 | const filenameToMid = (function () {
|
21 | if (pathUtil.sep === '/') {
|
22 | return function (filename) {
|
23 | return filename;
|
24 | };
|
25 | }
|
26 | else {
|
27 | const separatorExpression = new RegExp(pathUtil.sep.replace('\\', '\\\\'), 'g');
|
28 | return function (filename) {
|
29 | return filename.replace(separatorExpression, '/');
|
30 | };
|
31 | }
|
32 | })();
|
33 | |
34 |
|
35 |
|
36 |
|
37 |
|
38 | function getError(diagnostics) {
|
39 | let message = 'Declaration generation failed';
|
40 | diagnostics.forEach(function (diagnostic) {
|
41 |
|
42 |
|
43 |
|
44 | if (diagnostic.file) {
|
45 | const position = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
46 | message +=
|
47 | `\n${diagnostic.file.fileName}(${position.line + 1},${position.character + 1}): ` +
|
48 | `error TS${diagnostic.code}: ${diagnostic.messageText}`;
|
49 | }
|
50 | else {
|
51 | message += `\nerror TS${diagnostic.code}: ${diagnostic.messageText}`;
|
52 | }
|
53 | });
|
54 | const error = new Error(message);
|
55 | error.name = 'EmitterError';
|
56 | return error;
|
57 | }
|
58 | function getFilenames(baseDir, files) {
|
59 | return files.map(function (filename) {
|
60 | const resolvedFilename = pathUtil.resolve(filename);
|
61 | if (resolvedFilename.indexOf(baseDir) === 0) {
|
62 | return resolvedFilename;
|
63 | }
|
64 | return pathUtil.resolve(baseDir, filename);
|
65 | });
|
66 | }
|
67 | function processTree(sourceFile, replacer) {
|
68 | let code = '';
|
69 | let cursorPosition = 0;
|
70 | function skip(node) {
|
71 | cursorPosition = node.end;
|
72 | }
|
73 | function readThrough(node) {
|
74 | code += sourceFile.text.slice(cursorPosition, node.pos);
|
75 | cursorPosition = node.pos;
|
76 | }
|
77 | function visit(node) {
|
78 | readThrough(node);
|
79 | const replacement = replacer(node);
|
80 | if (replacement != null) {
|
81 | code += replacement;
|
82 | skip(node);
|
83 | }
|
84 | else {
|
85 | ts.forEachChild(node, visit);
|
86 | }
|
87 | }
|
88 | visit(sourceFile);
|
89 | code += sourceFile.text.slice(cursorPosition);
|
90 | return code;
|
91 | }
|
92 | |
93 |
|
94 |
|
95 |
|
96 |
|
97 | function getTSConfig(fileName) {
|
98 |
|
99 |
|
100 | const configText = fs.readFileSync(fileName, { encoding: 'utf8' });
|
101 | const result = ts.parseConfigFileTextToJson(fileName, configText);
|
102 | if (result.error) {
|
103 | throw getError([result.error]);
|
104 | }
|
105 | const configObject = result.config;
|
106 | const configParseResult = ts.parseJsonConfigFileContent(configObject, ts.sys, pathUtil.dirname(fileName));
|
107 | if (configParseResult.errors && configParseResult.errors.length) {
|
108 | throw getError(configParseResult.errors);
|
109 | }
|
110 | return [
|
111 | configParseResult.fileNames,
|
112 | configParseResult.options
|
113 | ];
|
114 | }
|
115 | function isNodeKindImportDeclaration(value) {
|
116 | return value && value.kind === ts.SyntaxKind.ImportDeclaration;
|
117 | }
|
118 | function isNodeKindExternalModuleReference(value) {
|
119 | return value && value.kind === ts.SyntaxKind.ExternalModuleReference;
|
120 | }
|
121 | function isNodeKindStringLiteral(value) {
|
122 | return value && value.kind === ts.SyntaxKind.StringLiteral;
|
123 | }
|
124 | function isNodeKindExportDeclaration(value) {
|
125 | return value && value.kind === ts.SyntaxKind.ExportDeclaration;
|
126 | }
|
127 | function isNodeKindExportAssignment(value) {
|
128 | return value && value.kind === ts.SyntaxKind.ExportAssignment;
|
129 | }
|
130 | function isNodeKindModuleDeclaration(value) {
|
131 | return value && value.kind === ts.SyntaxKind.ModuleDeclaration;
|
132 | }
|
133 | function generate(options) {
|
134 | if (Boolean(options.main) !== Boolean(options.name)) {
|
135 | if (Boolean(options.name)) {
|
136 |
|
137 |
|
138 |
|
139 |
|
140 | throw new Error(`name and main must be used together. Perhaps you want prefix instead of
|
141 | name? In dts-generator version 2.1, name did double duty as the option to
|
142 | use to prefix module names with, but in >=2.2 the name option was split
|
143 | into two; prefix is what is now used to prefix imports and module names
|
144 | in the output.`);
|
145 | }
|
146 | else {
|
147 | throw new Error('name and main must be used together.');
|
148 | }
|
149 | }
|
150 | const noop = function () { };
|
151 | const sendMessage = options.sendMessage || noop;
|
152 | const verboseMessage = options.verbose ? sendMessage : noop;
|
153 | let compilerOptions = {};
|
154 | let files = options.files;
|
155 | |
156 |
|
157 | if (options.project || !options.files || options.files.length === 0) {
|
158 | verboseMessage(`project = "${options.project || options.baseDir}"`);
|
159 |
|
160 |
|
161 |
|
162 |
|
163 | let tsconfigFilename;
|
164 | if (Boolean(options.project)) {
|
165 | if (fs.lstatSync(options.project).isDirectory()) {
|
166 | tsconfigFilename = pathUtil.join(options.project, 'tsconfig.json');
|
167 | }
|
168 | else {
|
169 |
|
170 | tsconfigFilename = options.project;
|
171 | }
|
172 | }
|
173 | else {
|
174 | tsconfigFilename = pathUtil.join(options.baseDir, 'tsconfig.json');
|
175 | }
|
176 | if (fs.existsSync(tsconfigFilename)) {
|
177 | verboseMessage(` parsing "${tsconfigFilename}"`);
|
178 | [files, compilerOptions] = getTSConfig(tsconfigFilename);
|
179 | }
|
180 | else {
|
181 | sendMessage(`No "tsconfig.json" found at "${tsconfigFilename}"!`);
|
182 | return new Promise(function ({}, reject) {
|
183 | reject(new SyntaxError('Unable to resolve configuration.'));
|
184 | });
|
185 | }
|
186 | }
|
187 | const eol = options.eol || os.EOL;
|
188 | const nonEmptyLineStart = new RegExp(eol + '(?!' + eol + '|$)', 'g');
|
189 | const indent = options.indent === undefined ? '\t' : options.indent;
|
190 |
|
191 |
|
192 | compilerOptions.declaration = true;
|
193 | compilerOptions.target = compilerOptions.target || ts.ScriptTarget.Latest;
|
194 | compilerOptions.moduleResolution = compilerOptions.moduleResolution || options.moduleResolution;
|
195 | compilerOptions.outDir = compilerOptions.outDir || options.outDir;
|
196 |
|
197 | const baseDir = pathUtil.resolve(compilerOptions.rootDir || options.project || options.baseDir);
|
198 | const outDir = compilerOptions.outDir;
|
199 | verboseMessage(`baseDir = "${baseDir}"`);
|
200 | verboseMessage(`target = ${compilerOptions.target}`);
|
201 | verboseMessage(`outDir = ${compilerOptions.outDir}`);
|
202 | verboseMessage(`rootDir = ${compilerOptions.rootDir}`);
|
203 | verboseMessage(`moduleResolution = ${compilerOptions.moduleResolution}`);
|
204 | const filenames = getFilenames(baseDir, files);
|
205 | verboseMessage('filenames:');
|
206 | filenames.forEach(name => { verboseMessage(' ' + name); });
|
207 | const excludesMap = {};
|
208 | options.exclude = options.exclude || ['node_modules/**/*.d.ts'];
|
209 | options.exclude && options.exclude.forEach(function (filename) {
|
210 | glob.sync(filename, { cwd: baseDir }).forEach(function (globFileName) {
|
211 | excludesMap[filenameToMid(pathUtil.resolve(baseDir, globFileName))] = true;
|
212 | });
|
213 | });
|
214 | if (options.exclude) {
|
215 | verboseMessage('exclude:');
|
216 | options.exclude.forEach(name => { verboseMessage(' ' + name); });
|
217 | }
|
218 | mkdirp.sync(pathUtil.dirname(options.out));
|
219 | |
220 |
|
221 | const output = fs.createWriteStream(options.out, { mode: parseInt('644', 8) });
|
222 | const host = ts.createCompilerHost(compilerOptions);
|
223 | const program = ts.createProgram(filenames, compilerOptions, host);
|
224 | function writeFile(filename, data) {
|
225 |
|
226 | if (filename.slice(-DTSLEN) !== '.d.ts') {
|
227 | return;
|
228 | }
|
229 | writeDeclaration(ts.createSourceFile(filename, data, compilerOptions.target, true), true);
|
230 | }
|
231 | let declaredExternalModules = [];
|
232 | return new Promise(function (resolve, reject) {
|
233 | output.on('close', () => { resolve(undefined); });
|
234 | output.on('error', reject);
|
235 | if (options.externs) {
|
236 | options.externs.forEach(function (path) {
|
237 | sendMessage(`Writing external dependency ${path}`);
|
238 | output.write(`/// <reference path="${path}" />` + eol);
|
239 | });
|
240 | }
|
241 | if (options.types) {
|
242 | options.types.forEach(function (type) {
|
243 | sendMessage(`Writing external @types package dependency ${type}`);
|
244 | output.write(`/// <reference types="${type}" />` + eol);
|
245 | });
|
246 | }
|
247 | sendMessage('processing:');
|
248 | let mainExportDeclaration = false;
|
249 | let mainExportAssignment = false;
|
250 | let foundMain = false;
|
251 | program.getSourceFiles().forEach(function (sourceFile) {
|
252 | processTree(sourceFile, function (node) {
|
253 | if (isNodeKindModuleDeclaration(node)) {
|
254 | const name = node.name;
|
255 | if (isNodeKindStringLiteral(name)) {
|
256 | declaredExternalModules.push(name.text);
|
257 | }
|
258 | }
|
259 | return null;
|
260 | });
|
261 | });
|
262 | program.getSourceFiles().some(function (sourceFile) {
|
263 |
|
264 |
|
265 | if (pathUtil.normalize(sourceFile.fileName).indexOf(baseDir + pathUtil.sep) !== 0) {
|
266 | return;
|
267 | }
|
268 | if (excludesMap[filenameToMid(pathUtil.normalize(sourceFile.fileName))]) {
|
269 | return;
|
270 | }
|
271 | sendMessage(` ${sourceFile.fileName}`);
|
272 |
|
273 | if (sourceFile.fileName.slice(-DTSLEN) === '.d.ts') {
|
274 | writeDeclaration(sourceFile, false);
|
275 | return;
|
276 | }
|
277 |
|
278 | if (options.main && options.main === (options.prefix + filenameToMid(sourceFile.fileName.slice(baseDir.length, -3)))) {
|
279 | foundMain = true;
|
280 | ts.forEachChild(sourceFile, function (node) {
|
281 | mainExportDeclaration = mainExportDeclaration || isNodeKindExportDeclaration(node);
|
282 | mainExportAssignment = mainExportAssignment || isNodeKindExportAssignment(node);
|
283 | });
|
284 | }
|
285 | const emitOutput = program.emit(sourceFile, writeFile);
|
286 | if (emitOutput.emitSkipped || emitOutput.diagnostics.length > 0) {
|
287 | reject(getError(emitOutput.diagnostics
|
288 | .concat(program.getSemanticDiagnostics(sourceFile))
|
289 | .concat(program.getSyntacticDiagnostics(sourceFile))
|
290 | .concat(program.getDeclarationDiagnostics(sourceFile))));
|
291 | return true;
|
292 | }
|
293 | });
|
294 | if (options.main && !foundMain) {
|
295 | throw new Error(`main module ${options.main} was not found`);
|
296 | }
|
297 | if (options.main) {
|
298 | output.write(`declare module '${options.name}' {` + eol + indent);
|
299 | if (compilerOptions.target >= ts.ScriptTarget.ES2015) {
|
300 | if (mainExportAssignment) {
|
301 | output.write(`export {default} from '${options.main}';` + eol + indent);
|
302 | }
|
303 | if (mainExportDeclaration) {
|
304 | output.write(`export * from '${options.main}';` + eol);
|
305 | }
|
306 | }
|
307 | else {
|
308 | output.write(`import main = require('${options.main}');` + eol + indent);
|
309 | output.write('export = main;' + eol);
|
310 | }
|
311 | output.write('}' + eol);
|
312 | sendMessage(`Aliased main module ${options.name} to ${options.main}`);
|
313 | }
|
314 | sendMessage(`output to "${options.out}"`);
|
315 | output.end();
|
316 | });
|
317 | function writeDeclaration(declarationFile, isOutput) {
|
318 |
|
319 | const filename = pathUtil.resolve(declarationFile.fileName);
|
320 |
|
321 |
|
322 |
|
323 |
|
324 |
|
325 | const outputDir = (isOutput && Boolean(outDir)) ? pathUtil.resolve(outDir) : baseDir;
|
326 | const sourceModuleId = filenameToMid(filename.slice(outputDir.length + 1, -DTSLEN));
|
327 | const currentModuleId = filenameToMid(filename.slice(outputDir.length + 1, -DTSLEN));
|
328 | function resolveModuleImport(moduleId) {
|
329 | const isDeclaredExternalModule = declaredExternalModules.indexOf(moduleId) !== -1;
|
330 | let resolved;
|
331 | if (options.resolveModuleImport) {
|
332 | resolved = options.resolveModuleImport({
|
333 | importedModuleId: moduleId,
|
334 | currentModuleId: currentModuleId,
|
335 | isDeclaredExternalModule: isDeclaredExternalModule
|
336 | });
|
337 | }
|
338 | if (!resolved) {
|
339 |
|
340 | if (moduleId.charAt(0) === '.') {
|
341 | resolved = filenameToMid(pathUtil.join(pathUtil.dirname(sourceModuleId), moduleId));
|
342 | }
|
343 | else {
|
344 | resolved = moduleId;
|
345 | }
|
346 |
|
347 |
|
348 |
|
349 |
|
350 |
|
351 |
|
352 |
|
353 | if (Boolean(options.prefix) && !isDeclaredExternalModule) {
|
354 | resolved = `${options.prefix}/${resolved}`;
|
355 | }
|
356 | }
|
357 | return resolved;
|
358 | }
|
359 | |
360 |
|
361 | if (declarationFile.externalModuleIndicator) {
|
362 | let resolvedModuleId = sourceModuleId;
|
363 | if (options.resolveModuleId) {
|
364 | const resolveModuleIdResult = options.resolveModuleId({
|
365 | currentModuleId: currentModuleId
|
366 | });
|
367 | if (resolveModuleIdResult) {
|
368 | resolvedModuleId = resolveModuleIdResult;
|
369 | }
|
370 | else if (options.prefix) {
|
371 | resolvedModuleId = `${options.prefix}/${resolvedModuleId}`;
|
372 | }
|
373 | }
|
374 | else if (options.prefix) {
|
375 | resolvedModuleId = `${options.prefix}/${resolvedModuleId}`;
|
376 | }
|
377 | output.write('declare module \'' + resolvedModuleId + '\' {' + eol + indent);
|
378 | const content = processTree(declarationFile, function (node) {
|
379 | if (isNodeKindExternalModuleReference(node)) {
|
380 |
|
381 |
|
382 | const expression = node.expression;
|
383 |
|
384 |
|
385 | const resolved = resolveModuleImport(expression.text);
|
386 | return ` require('${resolved}')`;
|
387 | }
|
388 | else if (node.kind === ts.SyntaxKind.DeclareKeyword) {
|
389 | return '';
|
390 | }
|
391 | else if (isNodeKindStringLiteral(node) && node.parent &&
|
392 | (isNodeKindExportDeclaration(node.parent) || isNodeKindImportDeclaration(node.parent))) {
|
393 |
|
394 | const text = node.text;
|
395 | const resolved = resolveModuleImport(text);
|
396 | if (resolved) {
|
397 | return ` '${resolved}'`;
|
398 | }
|
399 | }
|
400 | });
|
401 | output.write(content.replace(nonEmptyLineStart, '$&' + indent));
|
402 | output.write(eol + '}' + eol);
|
403 | }
|
404 | else {
|
405 | output.write(declarationFile.text);
|
406 | }
|
407 | }
|
408 | }
|
409 | exports.default = generate;
|
410 | });
|