1 | 'use strict';
|
2 |
|
3 | const fs = require('fs');
|
4 | const path = require('path');
|
5 | const async = require('async');
|
6 | const merge = require('merge');
|
7 | const sorcery = require('sorcery');
|
8 |
|
9 | const FileReader = require('./file-reader');
|
10 |
|
11 | class TransformChain {
|
12 |
|
13 | constructor(transforms, cacheChain) {
|
14 | this.transforms = transforms;
|
15 | this.cacheChain = cacheChain;
|
16 | }
|
17 |
|
18 | _buildChain(localFilename, remoteFilename) {
|
19 | var result = [];
|
20 |
|
21 | var transforms = this.transforms.concat();
|
22 |
|
23 | var getDstFilename = function() {
|
24 | return {
|
25 | local: ((result[result.length - 1] || {}).srcFilename || {}).local || localFilename,
|
26 | remote: ((result[result.length - 1] || {}).srcFilename || {}).remote || remoteFilename
|
27 | };
|
28 | };
|
29 |
|
30 |
|
31 | do {
|
32 | var found = false;
|
33 | for (var idx in transforms) {
|
34 | var transform = transforms[idx];
|
35 | var dstFilename = getDstFilename();
|
36 | if (transform.canTransform(dstFilename.local)) {
|
37 | result.push({
|
38 | srcFilename: {
|
39 | local: transform.map(dstFilename.local),
|
40 | remote: transform.map(dstFilename.remote)
|
41 | },
|
42 | dstFilename: dstFilename,
|
43 | transform: transform
|
44 | });
|
45 | transforms.splice(idx, 1);
|
46 | found = true;
|
47 | break;
|
48 | }
|
49 | }
|
50 | if (!found) break;
|
51 | } while (true);
|
52 | result.push({
|
53 | srcFilename: getDstFilename(),
|
54 | dstFilename: getDstFilename(),
|
55 | transform: new FileReader()
|
56 | });
|
57 | return result.reverse();
|
58 | }
|
59 |
|
60 | render(localFilename, remoteFilename, ctx) {
|
61 | return new Promise((resolved, rejected) => {
|
62 | this.cacheChain.get(localFilename).then((result) => {
|
63 | if (result) return resolved(merge(result, { cached: true }));
|
64 | var renderChain = this._buildChain(localFilename, remoteFilename);
|
65 | async.forEachOfSeries(renderChain, (item, idx, next) => {
|
66 | var lastResult = (renderChain[idx - 1] || {}).result || {};
|
67 | item.transform.compile(item.srcFilename.local, lastResult.data, lastResult.map, ctx).then((itemResult) => {
|
68 | if (!itemResult) return resolved();
|
69 | async.reduce(itemResult.files || [], [], (files, file, next) => {
|
70 | fs.access(file, (err) => {
|
71 | next(null, files.concat(!err ? [file] : []));
|
72 | });
|
73 | }, (err, files) => {
|
74 | if (err) return rejected(err);
|
75 | item.result = merge(itemResult, {
|
76 | files: files.map((filename) => {
|
77 | return path.relative(path.dirname(localFilename), filename);
|
78 | }),
|
79 | modificationDate: new Date()
|
80 | });
|
81 | next();
|
82 | });
|
83 | }, next);
|
84 | }, (err) => {
|
85 | if (err) return rejected(err);
|
86 | var lastResult = renderChain[renderChain.length - 1].result;
|
87 | var files = renderChain.reduce((files, item) => {
|
88 | return (files || []).concat(item.result.files || []);
|
89 | }, []);
|
90 | var uniqueFiles = [];
|
91 | files.forEach((file) => {
|
92 | if (uniqueFiles.indexOf(file) == -1) uniqueFiles.push(file);
|
93 | });
|
94 | lastResult = merge(lastResult, {
|
95 | filename: localFilename,
|
96 | files: uniqueFiles
|
97 | });
|
98 | if (lastResult.map) {
|
99 | lastResult.map.sources = lastResult.map.files = lastResult.files;
|
100 | lastResult.map.file = path.basename(remoteFilename);
|
101 | lastResult.data = new Buffer(lastResult.data.toString().replace(/# sourceMappingURL=.+($| |\*)/, '# sourceMappingURL=' + path.basename(remoteFilename) + '.map'));
|
102 | }
|
103 | this.cacheChain.set(localFilename, lastResult).then(() => {
|
104 | resolved(merge(lastResult, { cached: false }));
|
105 | }, rejected);
|
106 | });
|
107 | }, rejected);
|
108 | });
|
109 | }
|
110 |
|
111 | }
|
112 |
|
113 | module.exports = exports = TransformChain;
|