UNPKG

13.8 kBJavaScriptView Raw
1"use strict";
2var __assign = (this && this.__assign) || function () {
3 __assign = Object.assign || function(t) {
4 for (var s, i = 1, n = arguments.length; i < n; i++) {
5 s = arguments[i];
6 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7 t[p] = s[p];
8 }
9 return t;
10 };
11 return __assign.apply(this, arguments);
12};
13Object.defineProperty(exports, "__esModule", { value: true });
14var Fs = require("fs-extra");
15var Path = require("path");
16var ReadlineSync = require("readline-sync");
17var semver = require("semver");
18var Tmp = require("tmp");
19var git_1 = require("./git");
20var paths_1 = require("./paths");
21var release_1 = require("./release");
22var submodule_1 = require("./submodule");
23var utils_1 = require("./utils");
24var defaultDumpFileName = 'tutorial.json';
25var headEnd = '[//]: # (head-end)\n';
26var footStart = '[//]: # (foot-start)\n';
27// Dumps tutorial into a JSON file
28// Output path defaults to cwd
29function dumpProject(out, options) {
30 if (out === void 0) { out = utils_1.Utils.cwd(); }
31 if (options === void 0) { options = {}; }
32 if (out instanceof Object && !(out instanceof String)) {
33 options = out;
34 out = utils_1.Utils.cwd();
35 }
36 options = __assign({ filter: options.filter, reject: options.reject }, options);
37 // Output path is relative to cwd
38 out = Path.resolve(utils_1.Utils.cwd(), out);
39 // If provided output path is a dir assume the dump file should be created inside of it
40 if (utils_1.Utils.exists(out, 'dir')) {
41 out = Path.join(out, defaultDumpFileName);
42 }
43 if (utils_1.Utils.exists(out, 'file')) {
44 options.override = options.override || ReadlineSync.keyInYN([
45 'Output path already exists.',
46 'Would you like to override it and continue?',
47 ].join('\n'));
48 if (!options.override) {
49 return;
50 }
51 }
52 console.log();
53 console.log("Dumping into " + out + "...");
54 console.log();
55 // Ensure submodules are updated
56 // Note that we don't necessarily have to init tortilla, which is useful if we wanna reset
57 // the submodules url before we proceed any further
58 submodule_1.Submodule.list().forEach(function (submodule) {
59 submodule_1.Submodule.update(submodule);
60 });
61 // Will recursively ensure dirs as well
62 Fs.ensureFileSync(out);
63 // Run command once
64 var tags = git_1.Git([
65 'log', '--tags', '--simplify-by-decoration', '--pretty=format:%ci %d'
66 ]).split('\n')
67 .filter(Boolean)
68 .map(function (line) {
69 return line.match(/^([^(]+) \(.*tag: ([^@]+@\d+\.\d+\.\d+).*\)$/) ||
70 line.match(/^([^(]+) \(.*tag: ([^@]+@next).*\)$/);
71 })
72 .filter(Boolean)
73 .map(function (_a) {
74 var str = _a[0], date = _a[1], name = _a[2];
75 return ({
76 date: date,
77 name: name,
78 });
79 })
80 .sort(function (a, b) {
81 return new Date(a.date) > new Date(b.date) ? -1 : 1;
82 });
83 var branchNames = tags
84 .map(function (tag) {
85 return tag.name.split('@')[0];
86 })
87 .reduce(function (prev, branchName) {
88 if (!prev.includes(branchName)) {
89 prev.push(branchName);
90 }
91 return prev;
92 }, []);
93 if (options.filter) {
94 branchNames = branchNames.filter(function (branchName) {
95 return options.filter.includes(branchName);
96 });
97 }
98 if (options.reject) {
99 branchNames = branchNames.filter(function (branchName) {
100 return !options.reject.includes(branchName);
101 });
102 }
103 var pack = Fs.readJsonSync(paths_1.Paths.npm.package);
104 // TODO: Update test data
105 // Will be defined per branch for compatibility reasons
106 var repoUrl = (typeof pack.repository === 'string' ? pack.repository :
107 typeof pack.repository === 'object' ? pack.repository.url :
108 '').replace(/\.git$/, '');
109 var dump = branchNames.map(function (branchName) {
110 var historyBranchName = branchName + "-history";
111 // Check if branch exists
112 try {
113 git_1.Git(['rev-parse', historyBranchName]);
114 // If not, create it
115 }
116 catch (e) {
117 git_1.Git.print(['branch', '--track', historyBranchName, "remotes/origin/" + historyBranchName]);
118 }
119 // Run command once
120 var releaseTags = tags
121 .filter(function (tag) {
122 return tag.name.match(branchName);
123 })
124 .map(function (tag) { return ({
125 date: tag.date,
126 version: tag.name.split('@').pop(),
127 }); });
128 var releases = releaseTags.map(function (releaseTag, releaseIndex) {
129 var prevReleaseTag = releaseTags[releaseIndex + 1] || {};
130 var tagName = branchName + "@" + releaseTag.version;
131 var tagRevision = git_1.Git(['rev-parse', tagName]);
132 var historyRevision = git_1.Git([
133 'log', historyBranchName, "--grep=^" + tagName + ":", '--format=%H',
134 ]).split('\n')
135 .filter(Boolean)
136 .pop();
137 // Instead of printing diff to stdout we will receive it as a buffer
138 var changesDiff = release_1.Release.diff(prevReleaseTag.version, releaseTag.version, null, {
139 branch: branchName,
140 pipe: true
141 });
142 var manuals = Fs
143 .readdirSync(paths_1.Paths.manuals.views)
144 // There might also be transformed manuals inside nested dirs. If we will take
145 // these dirs into an account there will be additional unexpected manuals in the
146 // formed dump file
147 .filter(function (fileName) { return /\.md$/.test(fileName); })
148 .sort(utils_1.Utils.naturalSort)
149 .map(function (manualName, stepIndex) {
150 var format = '%H %s';
151 var stepLog;
152 var manualPath;
153 // Step
154 if (stepIndex) {
155 manualPath = Path.resolve(paths_1.Paths.manuals.views, manualName);
156 stepLog = git_1.Git([
157 'log', tagRevision, "--grep=^Step " + stepIndex + ":", "--format=" + format,
158 ]);
159 }
160 else {
161 manualPath = paths_1.Paths.readme;
162 stepLog = git_1.Git([
163 'log', git_1.Git.rootHash(tagRevision), "--format=" + format,
164 ]);
165 }
166 manualPath = Path.relative(utils_1.Utils.cwd(), manualPath);
167 stepLog = stepLog.split(' ');
168 var stepRevision = stepLog.shift();
169 var manualTitle = stepLog.join(' ');
170 var stepPack = JSON.parse(git_1.Git(['show', stepRevision + ":package.json"]));
171 var keywords = stepPack.keywords || [];
172 // Removing header and footer, since the view should be used externally on a
173 // different host which will make sure to show these two
174 var manualView = git_1.Git(['show', stepRevision + ":" + manualPath]);
175 if (manualView.includes(headEnd)) {
176 manualView = manualView
177 .split(headEnd)
178 .slice(1)
179 .join(headEnd);
180 }
181 if (manualView.includes(footStart)) {
182 manualView = manualView
183 .split(footStart)
184 .slice(0, -1)
185 .join(footStart);
186 }
187 manualView = manualView.trim();
188 return {
189 manualTitle: manualTitle,
190 stepRevision: stepRevision,
191 manualView: manualView,
192 keywords: keywords
193 };
194 });
195 return {
196 releaseVersion: releaseTag.version,
197 releaseDate: releaseTag.date,
198 tagName: tagName,
199 tagRevision: tagRevision,
200 historyRevision: historyRevision,
201 changesDiff: changesDiff,
202 manuals: manuals,
203 };
204 });
205 return {
206 repoUrl: repoUrl,
207 branchName: branchName,
208 historyBranchName: historyBranchName,
209 releases: releases,
210 };
211 });
212 Fs.writeJsonSync(out, dump, { spaces: 2 });
213 console.log();
214 console.log('Dump finished.');
215 console.log();
216}
217// TODO: Make client calculate the diff on a service worker
218// or serve the created HTML file (using SSR)
219function diffReleases(dump, srcTag, dstTag) {
220 var _a = srcTag.split('@'), srcBranchName = _a[0], srcReleaseVersion = _a[1];
221 var _b = dstTag.split('@'), dstBranchName = _b[0], dstReleaseVersion = _b[1];
222 if (!srcReleaseVersion && !dstReleaseVersion) {
223 srcReleaseVersion = srcBranchName;
224 dstReleaseVersion = dstBranchName;
225 srcBranchName = null;
226 dstBranchName = null;
227 }
228 if (semverEq(srcReleaseVersion, dstReleaseVersion)) {
229 return '';
230 }
231 // Test result will be used later on
232 var reversed = semverGt(srcReleaseVersion, dstReleaseVersion);
233 if (reversed) {
234 var temp = void 0;
235 temp = srcReleaseVersion;
236 srcReleaseVersion = dstReleaseVersion;
237 dstReleaseVersion = temp;
238 temp = srcBranchName;
239 srcBranchName = dstBranchName;
240 dstBranchName = temp;
241 }
242 // If an FS path was provided
243 if (typeof dump === 'string' || dump instanceof String) {
244 // Resolving path relative to cwd
245 // Note that this is the process cwd and not the project cwd
246 dump = Path.resolve(process.cwd(), dump);
247 // Parsing JSON
248 dump = Fs.readJSONSync(dump);
249 }
250 var srcDir = buildRelease(dump, srcReleaseVersion, srcBranchName);
251 var dstDir = buildRelease(dump, dstReleaseVersion, dstBranchName);
252 Fs.removeSync(srcDir.name + "/.git");
253 Fs.copySync(dstDir.name + "/.git", srcDir.name + "/.git");
254 var diff = utils_1.Utils.scopeEnv(function () {
255 git_1.Git(['add', '.']);
256 try {
257 git_1.Git(['commit', '-m', dstReleaseVersion]);
258 // No changes were made between releases
259 // Probably due to missing versions of submodules
260 }
261 catch (e) {
262 return '';
263 }
264 return reversed
265 ? git_1.Git(['diff', 'HEAD^', 'HEAD'])
266 : git_1.Git(['diff', 'HEAD', 'HEAD^']);
267 }, {
268 TORTILLA_CWD: srcDir.name
269 });
270 srcDir.removeCallback();
271 dstDir.removeCallback();
272 return postTransformDiff(diff);
273}
274function buildRelease(dump, releaseVersion, branchName) {
275 // If no branch was provided, assuming this is a chunk
276 var chunk = branchName ? dump.find(function (c) { return c.branchName === branchName; }) : dump;
277 var releaseIndex = chunk.releases.findIndex(function (r) { return r.releaseVersion === releaseVersion; });
278 // Most recent release would come LAST
279 var releases = chunk.releases.slice(releaseIndex - chunk.releases.length).reverse();
280 var diffs = releases.map(function (r) { return r.changesDiff; }).filter(Boolean).map(preTransformDiff);
281 var dir = Tmp.dirSync({ unsafeCleanup: true });
282 utils_1.Utils.scopeEnv(function () {
283 git_1.Git(['init']);
284 diffs.forEach(function (diff) {
285 try {
286 git_1.Git(['apply'], {
287 input: diff
288 });
289 }
290 catch (e) {
291 e.message += " (version " + releaseVersion + ")";
292 throw e;
293 }
294 });
295 git_1.Git(['add', '.']);
296 git_1.Git(['commit', '-m', releaseVersion]);
297 }, {
298 TORTILLA_CWD: dir.name
299 });
300 return dir;
301}
302// TODO: Add tests
303// Turns binary files into placeholders so diff can be applied
304function preTransformDiff(diff) {
305 return diff
306 .replace(/Binary files \/dev\/null and b\/([^ ]+) differ/g, [
307 '--- /dev/null',
308 '+++ b/$1',
309 '@@ -0,0 +1 @@',
310 '+__tortilla_bin__',
311 ].join('\n'))
312 .replace(/Binary files a\/([^ ]+) and \/dev\/null differ/g, [
313 '--- a/$1',
314 '+++ /dev/null',
315 '@@ -1 +0,0 @@',
316 '-__tortilla_bin__',
317 ].join('\n'))
318 .replace(/Binary files a\/([^ ]+) and b\/([^ ]+) differ/g, [
319 '--- a/$1',
320 '+++ b/$2',
321 '@@ -1 +1 @@',
322 '-__tortilla_bin__',
323 '+__tortilla_bin__',
324 ].join('\n'));
325}
326// Turns placeholders into binary files so diff can be loyal
327function postTransformDiff(diff) {
328 return diff
329 .replace(/--- \/dev\/null\n\+\+\+ b\/(.+)\n@@ -0,0 \+1 @@\n\+__tortilla_bin__/g, 'Binary files /dev/null and b/$1 differ')
330 .replace(/--- a\/(.+)\n\+\+\+ \/dev\/null\n@@ -1 \+0,0 @@\n-__tortilla_bin__/g, 'Binary files a/$1 and /dev/null differ')
331 .replace(/--- a\/(.+)\n\+\+\+ b\/(.+)\n@@ -1 \+1 @@\n-__tortilla_bin__\n\+__tortilla_bin__/g, 'Binary files a/$1 and b/$2 differ');
332}
333// Add vNext to semver.eq()
334function semverEq(src, dst) {
335 if (src === 'next' && dst === 'next') {
336 return true;
337 }
338 if (src === 'next') {
339 return false;
340 }
341 if (dst === 'next') {
342 return false;
343 }
344 return semver.eq(src, dst);
345}
346// Add vNext to semver.gt()
347function semverGt(src, dst) {
348 if (src === 'next' && dst === 'next') {
349 return false;
350 }
351 if (src === 'next') {
352 return true;
353 }
354 if (dst === 'next') {
355 return false;
356 }
357 return semver.gt(src, dst);
358}
359exports.Dump = {
360 create: dumpProject,
361 diffReleases: diffReleases
362};
363//# sourceMappingURL=dump.js.map
\No newline at end of file