1 | 'use strict';
|
2 | const chalk = require('chalk');
|
3 | const pad = require('pad-component');
|
4 | const wrap = require('wrap-ansi');
|
5 | const stringWidth = require('string-width');
|
6 | const stripAnsi = require('strip-ansi');
|
7 | const ansiStyles = require('ansi-styles');
|
8 | const ansiRegex = require('ansi-regex')();
|
9 | const cliBoxes = require('cli-boxes');
|
10 |
|
11 | const border = cliBoxes.round;
|
12 | const leftOffset = 17;
|
13 | const defaultGreeting =
|
14 | '\n _-----_ ' +
|
15 | '\n | | ' +
|
16 | '\n |' + chalk.red('--(o)--') + '| ' +
|
17 | '\n `---------´ ' +
|
18 | '\n ' + chalk.yellow('(') + ' _' + chalk.yellow('´U`') + '_ ' + chalk.yellow(')') + ' ' +
|
19 | '\n /___A___\\ /' +
|
20 | '\n ' + chalk.yellow('| ~ |') + ' ' +
|
21 | '\n __' + chalk.yellow('\'.___.\'') + '__ ' +
|
22 | '\n ´ ' + chalk.red('` |') + '° ' + chalk.red('´ Y') + ' ` ';
|
23 |
|
24 | module.exports = (message, options) => {
|
25 | message = (message || 'Welcome to Yeoman, ladies and gentlemen!').trim();
|
26 | options = options || {};
|
27 |
|
28 | |
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 | let maxLength = 24;
|
45 | const styledIndexes = {};
|
46 | let completedString = '';
|
47 | let topOffset = 4;
|
48 |
|
49 |
|
50 | const YEOMAN_CHARACTER_WIDTH = 17;
|
51 |
|
52 |
|
53 | const DEFAULT_TOP_FRAME_WIDTH = 28;
|
54 |
|
55 |
|
56 | let TOTAL_CHARACTERS_PER_LINE = YEOMAN_CHARACTER_WIDTH + DEFAULT_TOP_FRAME_WIDTH;
|
57 |
|
58 |
|
59 | const MAX_MESSAGE_LINES_BEFORE_OVERFLOW = 7;
|
60 |
|
61 | if (options.maxLength) {
|
62 | maxLength = stripAnsi(message).toLowerCase().split(' ').sort()[0].length;
|
63 |
|
64 | if (maxLength < options.maxLength) {
|
65 | maxLength = options.maxLength;
|
66 | TOTAL_CHARACTERS_PER_LINE = maxLength + YEOMAN_CHARACTER_WIDTH + topOffset;
|
67 | }
|
68 | }
|
69 |
|
70 | const regExNewLine = new RegExp(`\\s{${maxLength}}`);
|
71 | const borderHorizontal = border.horizontal.repeat(maxLength + 2);
|
72 |
|
73 | const frame = {
|
74 | top: border.topLeft + borderHorizontal + border.topRight,
|
75 | side: ansiStyles.reset.open + border.vertical + ansiStyles.reset.open,
|
76 | bottom: ansiStyles.reset.open + border.bottomLeft + borderHorizontal + border.bottomRight
|
77 | };
|
78 |
|
79 | message.replace(ansiRegex, (match, offset) => {
|
80 | Object.keys(styledIndexes).forEach(key => {
|
81 | offset -= styledIndexes[key].length;
|
82 | });
|
83 |
|
84 | styledIndexes[offset] = styledIndexes[offset] ? styledIndexes[offset] + match : match;
|
85 | });
|
86 |
|
87 | return wrap(stripAnsi(message), maxLength, {hard: true})
|
88 | .split(/\n/)
|
89 | .reduce((greeting, str, index, array) => {
|
90 | if (!regExNewLine.test(str)) {
|
91 | str = str.trim();
|
92 | }
|
93 |
|
94 | completedString += str;
|
95 |
|
96 | str = completedString
|
97 | .substr(completedString.length - str.length)
|
98 | .replace(/./g, (char, charIndex) => {
|
99 | if (index > 0) {
|
100 | charIndex += completedString.length - str.length + index;
|
101 | }
|
102 |
|
103 | let hasContinuedStyle = 0;
|
104 | let continuedStyle;
|
105 |
|
106 | Object.keys(styledIndexes).forEach(offset => {
|
107 | if (charIndex > offset) {
|
108 | hasContinuedStyle++;
|
109 | continuedStyle = styledIndexes[offset];
|
110 | }
|
111 |
|
112 | if (hasContinuedStyle === 1 && charIndex < offset) {
|
113 | hasContinuedStyle++;
|
114 | }
|
115 | });
|
116 |
|
117 | if (styledIndexes[charIndex]) {
|
118 | return styledIndexes[charIndex] + char;
|
119 | } else if (hasContinuedStyle >= 2) {
|
120 | return continuedStyle + char;
|
121 | }
|
122 |
|
123 | return char;
|
124 | })
|
125 | .trim();
|
126 |
|
127 | const paddedString = pad({
|
128 | length: stringWidth(str),
|
129 | valueOf() {
|
130 | return ansiStyles.reset.open + str + ansiStyles.reset.open;
|
131 | }
|
132 | }, maxLength);
|
133 |
|
134 | if (index === 0) {
|
135 |
|
136 |
|
137 | if (array.length === 2) {
|
138 | topOffset -= 1;
|
139 | }
|
140 |
|
141 | if (array.length >= 3) {
|
142 | topOffset -= 2;
|
143 | }
|
144 |
|
145 |
|
146 |
|
147 |
|
148 | if (array.length > MAX_MESSAGE_LINES_BEFORE_OVERFLOW) {
|
149 | const emptyLines = Math.ceil((array.length - MAX_MESSAGE_LINES_BEFORE_OVERFLOW) / 2);
|
150 |
|
151 | for (let i = 0; i < emptyLines; i++) {
|
152 | greeting.unshift('');
|
153 | }
|
154 |
|
155 | frame.top = pad.left(frame.top, TOTAL_CHARACTERS_PER_LINE);
|
156 | }
|
157 |
|
158 | greeting[topOffset - 1] += frame.top;
|
159 | }
|
160 |
|
161 | greeting[index + topOffset] =
|
162 | (greeting[index + topOffset] || pad.left('', leftOffset)) +
|
163 | frame.side + ' ' + paddedString + ' ' + frame.side;
|
164 |
|
165 | if (array.length === index + 1) {
|
166 | greeting[index + topOffset + 1] =
|
167 | (greeting[index + topOffset + 1] || pad.left('', leftOffset)) +
|
168 | frame.bottom;
|
169 | }
|
170 |
|
171 | return greeting;
|
172 | }, defaultGreeting.split(/\n/))
|
173 | .join('\n') + '\n';
|
174 | };
|