UNPKG

11.6 kBPlain TextView Raw
1import * as fs from 'fs-extra';
2import * as _ from 'lodash';
3import * as path from 'path';
4import { ts } from 'ts-simple-ast';
5
6import { LinkParser } from './link-parser';
7
8import { logger } from './logger';
9
10import { AngularLifecycleHooks } from './angular-lifecycles-hooks';
11import { kindToType } from './kind-to-type';
12
13const getCurrentDirectory = ts.sys.getCurrentDirectory;
14const useCaseSensitiveFileNames = ts.sys.useCaseSensitiveFileNames;
15const newLine = ts.sys.newLine;
16const marked = require('marked');
17
18export function getNewLine(): string {
19 return newLine;
20}
21
22export function cleanNameWithoutSpaceAndToLowerCase(name: string): string {
23 return name.toLowerCase().replace(/ /g, '-');
24}
25
26export function getCanonicalFileName(fileName: string): string {
27 return useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
28}
29
30export const formatDiagnosticsHost: ts.FormatDiagnosticsHost = {
31 getCurrentDirectory,
32 getCanonicalFileName,
33 getNewLine
34};
35
36export function markedtags(tags: Array<any>) {
37 let mtags = tags;
38 _.forEach(mtags, tag => {
39 tag.comment = marked(LinkParser.resolveLinks(tag.comment));
40 });
41 return mtags;
42}
43
44export function mergeTagsAndArgs(args: Array<any>, jsdoctags?: Array<any>): Array<any> {
45 let margs = _.cloneDeep(args);
46 _.forEach(margs, arg => {
47 arg.tagName = {
48 text: 'param'
49 };
50 if (jsdoctags) {
51 _.forEach(jsdoctags, jsdoctag => {
52 if (jsdoctag.name && jsdoctag.name.text === arg.name) {
53 arg.tagName = jsdoctag.tagName;
54 arg.name = jsdoctag.name;
55 arg.comment = jsdoctag.comment;
56 arg.typeExpression = jsdoctag.typeExpression;
57 }
58 });
59 }
60 });
61 // Add example & returns & private
62 if (jsdoctags) {
63 _.forEach(jsdoctags, jsdoctag => {
64 if (
65 jsdoctag.tagName &&
66 (jsdoctag.tagName.text === 'example' || jsdoctag.tagName.text === 'private')
67 ) {
68 margs.push({
69 tagName: jsdoctag.tagName,
70 comment: jsdoctag.comment
71 });
72 }
73 if (
74 jsdoctag.tagName &&
75 (jsdoctag.tagName.text === 'returns' || jsdoctag.tagName.text === 'return')
76 ) {
77 let ret = {
78 tagName: jsdoctag.tagName,
79 comment: jsdoctag.comment
80 };
81 if (jsdoctag.typeExpression && jsdoctag.typeExpression.type) {
82 ret.returnType = kindToType(jsdoctag.typeExpression.type.kind);
83 }
84 margs.push(ret);
85 }
86 });
87 }
88 return margs;
89}
90
91export function readConfig(configFile: string): any {
92 let result = ts.readConfigFile(configFile, ts.sys.readFile);
93 if (result.error) {
94 let message = ts.formatDiagnostics([result.error], formatDiagnosticsHost);
95 throw new Error(message);
96 }
97 return result.config;
98}
99
100export function stripBom(source: string): string {
101 if (source.charCodeAt(0) === 0xfeff) {
102 return source.slice(1);
103 }
104 return source;
105}
106
107export function hasBom(source: string): boolean {
108 return source.charCodeAt(0) === 0xfeff;
109}
110
111export function handlePath(files: Array<string>, cwd: string): Array<string> {
112 let _files = files;
113 let i = 0;
114 let len = files.length;
115
116 for (i; i < len; i++) {
117 if (files[i].indexOf(cwd) === -1) {
118 files[i] = path.resolve(cwd + path.sep + files[i]);
119 }
120 }
121
122 return _files;
123}
124
125export function cleanLifecycleHooksFromMethods(methods: Array<any>): Array<any> {
126 let result = [];
127 if (typeof methods !== 'undefined') {
128 let i = 0;
129 let len = methods.length;
130 for (i; i < len; i++) {
131 if (!(methods[i].name in AngularLifecycleHooks)) {
132 result.push(methods[i]);
133 }
134 }
135 }
136 return result;
137}
138
139export function cleanSourcesForWatch(list) {
140 return list.filter(element => {
141 if (fs.existsSync(process.cwd() + path.sep + element)) {
142 return element;
143 }
144 });
145}
146
147export function getNamesCompareFn(name?) {
148 /**
149 * Copyright https://github.com/ng-bootstrap/ng-bootstrap
150 */
151 name = name || 'name';
152 const t = (a, b) => {
153 if (a[name]) {
154 return a[name].localeCompare(b[name]);
155 } else {
156 return 0;
157 }
158 };
159 return t;
160}
161
162export function isIgnore(member): boolean {
163 if (member.jsDoc) {
164 for (const doc of member.jsDoc) {
165 if (doc.tags) {
166 for (const tag of doc.tags) {
167 if (tag.tagName.text.indexOf('ignore') > -1) {
168 return true;
169 }
170 }
171 }
172 }
173 }
174 return false;
175}
176
177// https://tc39.github.io/ecma262/#sec-array.prototype.includes
178if (!Array.prototype.includes) {
179 Object.defineProperty(Array.prototype, 'includes', {
180 value: function(searchElement, fromIndex) {
181 if (this == null) {
182 throw new TypeError('"this" is null or not defined');
183 }
184
185 // 1. Let O be ? ToObject(this value).
186 let o = Object(this);
187
188 // 2. Let len be ? ToLength(? Get(O, "length")).
189 let len = o.length >>> 0;
190
191 // 3. If len is 0, return false.
192 if (len === 0) {
193 return false;
194 }
195
196 // 4. Let n be ? ToInteger(fromIndex).
197 // (If fromIndex is undefined, this step produces the value 0.)
198 let n = fromIndex | 0;
199
200 // 5. If n ≥ 0, then
201 // a. Let k be n.
202 // 6. Else n < 0,
203 // a. Let k be len + n.
204 // b. If k < 0, let k be 0.
205 let k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
206
207 function sameValueZero(x, y) {
208 return (
209 x === y ||
210 (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y))
211 );
212 }
213
214 // 7. Repeat, while k < len
215 while (k < len) {
216 // a. Let elementK be the result of ? Get(O, ! ToString(k)).
217 // b. If SameValueZero(searchElement, elementK) is true, return true.
218 if (sameValueZero(o[k], searchElement)) {
219 return true;
220 }
221 // c. Increase k by 1.
222 k++;
223 }
224
225 // 8. Return false
226 return false;
227 }
228 });
229}
230
231export function findMainSourceFolder(files: string[]) {
232 let mainFolder = '';
233 let mainFolderCount = 0;
234 let rawFolders = files.map(filepath => {
235 let shortPath = filepath.replace(process.cwd() + path.sep, '');
236 return path.dirname(shortPath);
237 });
238 let folders = {};
239 rawFolders = _.uniq(rawFolders);
240
241 for (let i = 0; i < rawFolders.length; i++) {
242 let sep = rawFolders[i].split(path.sep);
243 sep.forEach(folder => {
244 if (folders[folder]) {
245 folders[folder] += 1;
246 } else {
247 folders[folder] = 1;
248 }
249 });
250 }
251 for (let f in folders) {
252 if (folders[f] > mainFolderCount) {
253 mainFolderCount = folders[f];
254 mainFolder = f;
255 }
256 }
257 return mainFolder;
258}
259
260// Create a compilerHost object to allow the compiler to read and write files
261export function compilerHost(transpileOptions: any): ts.CompilerHost {
262 const inputFileName =
263 transpileOptions.fileName || (transpileOptions.jsx ? 'module.tsx' : 'module.ts');
264
265 const toReturn: ts.CompilerHost = {
266 getSourceFile: (fileName: string) => {
267 if (fileName.lastIndexOf('.ts') !== -1 || fileName.lastIndexOf('.js') !== -1) {
268 if (fileName === 'lib.d.ts') {
269 return undefined;
270 }
271 if (fileName.substr(-5) === '.d.ts') {
272 return undefined;
273 }
274
275 if (path.isAbsolute(fileName) === false) {
276 fileName = path.join(transpileOptions.tsconfigDirectory, fileName);
277 }
278 if (!fs.existsSync(fileName)) {
279 return undefined;
280 }
281
282 let libSource = '';
283
284 try {
285 libSource = fs.readFileSync(fileName).toString();
286
287 if (hasBom(libSource)) {
288 libSource = stripBom(libSource);
289 }
290 } catch (e) {
291 logger.debug(e, fileName);
292 }
293
294 return ts.createSourceFile(fileName, libSource, transpileOptions.target, false);
295 }
296 return undefined;
297 },
298 writeFile: (name, text) => {},
299 getDefaultLibFileName: () => 'lib.d.ts',
300 useCaseSensitiveFileNames: () => false,
301 getCanonicalFileName: fileName => fileName,
302 getCurrentDirectory: () => '',
303 getNewLine: () => '\n',
304 fileExists: (fileName): boolean => fileName === inputFileName,
305 readFile: () => '',
306 directoryExists: () => true,
307 getDirectories: () => []
308 };
309
310 return toReturn;
311}
312
313export function detectIndent(str, count): string {
314 let stripIndent = (stripedString: string) => {
315 const match = stripedString.match(/^[ \t]*(?=\S)/gm);
316
317 if (!match) {
318 return stripedString;
319 }
320
321 // TODO: use spread operator when targeting Node.js 6
322 const indent = Math.min.apply(Math, match.map(x => x.length)); // eslint-disable-line
323 const re = new RegExp(`^[ \\t]{${indent}}`, 'gm');
324
325 return indent > 0 ? stripedString.replace(re, '') : stripedString;
326 };
327
328 let repeating = (n, repeatString) => {
329 repeatString = repeatString === undefined ? ' ' : repeatString;
330
331 if (typeof repeatString !== 'string') {
332 throw new TypeError(
333 `Expected \`input\` to be a \`string\`, got \`${typeof repeatString}\``
334 );
335 }
336
337 if (n < 0) {
338 throw new TypeError(`Expected \`count\` to be a positive finite number, got \`${n}\``);
339 }
340
341 let ret = '';
342
343 do {
344 if (n & 1) {
345 ret += repeatString;
346 }
347
348 repeatString += repeatString;
349 } while ((n >>= 1));
350
351 return ret;
352 };
353
354 let indentString = (indentedString, indentCount) => {
355 let indent = ' ';
356 indentCount = indentCount === undefined ? 1 : indentCount;
357
358 if (typeof indentedString !== 'string') {
359 throw new TypeError(
360 `Expected \`input\` to be a \`string\`, got \`${typeof indentedString}\``
361 );
362 }
363
364 if (typeof indentCount !== 'number') {
365 throw new TypeError(
366 `Expected \`count\` to be a \`number\`, got \`${typeof indentCount}\``
367 );
368 }
369
370 if (typeof indent !== 'string') {
371 throw new TypeError(`Expected \`indent\` to be a \`string\`, got \`${typeof indent}\``);
372 }
373
374 if (indentCount === 0) {
375 return indentedString;
376 }
377
378 indent = indentCount > 1 ? repeating(indentCount, indent) : indent;
379
380 return indentedString.replace(/^(?!\s*$)/gm, indent);
381 };
382
383 return indentString(stripIndent(str), count || 0);
384}