1 | var path = require('path');
|
2 | var mkdirp = require('mkdirp');
|
3 | var once = require('once');
|
4 | var fs = require('fs');
|
5 | var SlJsInfra = require('sl-js-infra').SlJsInfra;
|
6 | var fileAndFolderUtils = require('../file-and-folder-utils');
|
7 | var ValidationUtils = SlJsInfra.Utils.ValidationUtils;
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | function FileInstrumenter(opts, preambleHeader, instrumenter, name, IgnoredFilesHandler, logger) {
|
18 | ValidationUtils.verifyNotNullOrEmpty(opts, "opts");
|
19 | ValidationUtils.verifyNotNullOrEmpty(preambleHeader, "preambleHeader");
|
20 | ValidationUtils.verifyNotNullOrEmpty(instrumenter, "instrumenter");
|
21 | ValidationUtils.verifyNotNullOrEmpty(name, "name");
|
22 | ValidationUtils.verifyNotNullOrEmpty(IgnoredFilesHandler, "IgnoredFilesHandler");
|
23 | ValidationUtils.verifyNotNullOrEmpty(logger, "logger");
|
24 |
|
25 | this.opts = opts;
|
26 | this.preambleHeader = preambleHeader;
|
27 | this.instrumenter = instrumenter;
|
28 | this.name = name;
|
29 | this.IgnoredFilesHandler = IgnoredFilesHandler;
|
30 | this.logger = logger;
|
31 | }
|
32 |
|
33 |
|
34 | FileInstrumenter.prototype.processSingleFile = function (callback) {
|
35 | ValidationUtils.verifyNotNullOrEmpty(callback, "callback");
|
36 |
|
37 | this.logger.debug("Processing single file. File: '" + this.name + "'.");
|
38 |
|
39 | var inputFile = this._getInputFile()
|
40 | var outputFile = this._getOutputFile();
|
41 | var inputFileExtension = path.extname(inputFile);
|
42 | var isValidExtension = this.opts.extensionsToInstrument[inputFileExtension];
|
43 | var shouldInstrumentFile = isValidExtension && this._isFileAcceptedBySlIgnore();
|
44 | var outputDir = path.dirname(outputFile);
|
45 | var _this = this;
|
46 |
|
47 | callback = once(callback);
|
48 | mkdirp.sync(outputDir);
|
49 |
|
50 | if (fs.statSync(inputFile).isDirectory()) {
|
51 | this.logger.info("Input file is a directory. No need to process");
|
52 | return callback(null, this.name);
|
53 | }
|
54 |
|
55 | if (shouldInstrumentFile) {
|
56 | return this._instrumentFile(inputFile, outputFile, this.name, this.preambleHeader, callback);
|
57 | }else{
|
58 | fileAndFolderUtils.copyFileAsIs(inputFile, outputFile, this.name, this.logger, function (err, name) {
|
59 | if(err && name && _this.opts.onFileCopyError != null){
|
60 | _this.opts.onFileCopyError(name, err);
|
61 | }
|
62 | else if (!err && name && _this.opts.onFileCopied != null) {
|
63 |
|
64 | _this.opts.onFileCopied(name);
|
65 | }
|
66 | return callback(err, name);
|
67 | });
|
68 | }
|
69 | }
|
70 |
|
71 |
|
72 | FileInstrumenter.prototype._onBeforeInstrumentation = function (fileContent) {
|
73 | fileContent = this._removeWebpackComments(fileContent);
|
74 | return fileContent;
|
75 | }
|
76 |
|
77 | FileInstrumenter.prototype._onAfterInstrumentation = function (instrumentedFileContent) {
|
78 | instrumentedFileContent = this._fixMissingSpaceAfterReturnStatement(instrumentedFileContent);
|
79 | return instrumentedFileContent;
|
80 | }
|
81 |
|
82 | FileInstrumenter.prototype._handleInstrumentationError = function (message, err, name, callback) {
|
83 | this.logger.error(message + " File: '" + name + "'. Error:", err);
|
84 | if (this.opts.onFileWithInstrumentationError != null){
|
85 | this.opts.onFileWithInstrumentationError({ file: name, error: err });
|
86 | }
|
87 |
|
88 | return callback(err, name);
|
89 | }
|
90 |
|
91 | FileInstrumenter.prototype._instrumentFile = function (inputFile, outputFile, name, preambleHeader, callback) {
|
92 | this.logger.debug("Instrumenting file '" + inputFile + "'.");
|
93 | var _this = this;
|
94 | fs.readFile(inputFile, 'utf8', function (err, fileContents) {
|
95 | if (err) {
|
96 | return _this._handleInstrumentationError("Failed reading file contents.", err, name, callback);
|
97 | }
|
98 |
|
99 | fileContents = _this._onBeforeInstrumentation(fileContents);
|
100 |
|
101 | _this.instrumenter.instrument(fileContents, inputFile, function (err, instrumentedContent){
|
102 | return _this.instrumentationCallback(err, instrumentedContent, preambleHeader, name, outputFile, callback);
|
103 | }, {
|
104 | filename: name,
|
105 | inputSourceMap: null
|
106 | });
|
107 | });
|
108 | }
|
109 |
|
110 |
|
111 |
|
112 | FileInstrumenter.prototype.instrumentationCallback = function (err, instrumentedContent, preambleHeader, name,
|
113 | outputFile, callback) {
|
114 | var _this = this;
|
115 | if (err) {
|
116 | return _this._handleInstrumentationError("Failed during instrumentation.", err, name, callback);
|
117 | }
|
118 | var newContents = preambleHeader.concat([instrumentedContent]).join("\n");
|
119 | newContents = _this._onAfterInstrumentation(newContents)
|
120 | fs.writeFile(outputFile, newContents, 'utf8', function (err) {
|
121 | if (err) {
|
122 | return _this._handleInstrumentationError("Failed while writing instrumented file.", err, name,
|
123 | callback);
|
124 | }
|
125 | if (_this.opts.onFileInstrumented != null) {
|
126 | _this.opts.onFileInstrumented(name);
|
127 | }
|
128 | return callback(null, name);
|
129 | });
|
130 | }
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 | FileInstrumenter.prototype._fixMissingSpaceAfterReturnStatement = function (input) {
|
140 | return input.replace(/;return</g, ";return <");
|
141 | }
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 | FileInstrumenter.prototype._removeWebpackComments = function (input) {
|
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 | var regex = new RegExp("\/[\*]{2,}\/", "g");
|
162 |
|
163 | while (true) {
|
164 |
|
165 | var currentMatch = regex.exec(input);
|
166 | if (!currentMatch)
|
167 | break;
|
168 | var matchIndex = currentMatch.index;
|
169 | var matchLength = currentMatch[0].length;
|
170 | var whiteSpace = this._createWhiteSpace(matchLength);
|
171 |
|
172 |
|
173 | input = this._replaceAt(input, matchIndex, whiteSpace);
|
174 | }
|
175 |
|
176 | return input;
|
177 | }
|
178 |
|
179 | FileInstrumenter.prototype._replaceAt = function (string, index, replacement) {
|
180 | return string.substr(0, index) + replacement + string.substr(index + replacement.length);
|
181 | }
|
182 |
|
183 | FileInstrumenter.prototype._createWhiteSpace = function (length) {
|
184 | var arr = new Array(++length);
|
185 | return arr.join(" ")
|
186 | }
|
187 |
|
188 | FileInstrumenter.prototype._isFileAcceptedBySlIgnore = function () {
|
189 | if (this.IgnoredFilesHandler.denies(this.name)){
|
190 | this.logger.info("File '%s' ignored by .slignore, will be copied as is", this.name);
|
191 | return false;
|
192 | }
|
193 | return true;
|
194 | }
|
195 |
|
196 |
|
197 | FileInstrumenter.prototype._getInputFile = function() {
|
198 | var folder = this.opts.sourceRoot;
|
199 | return path.resolve(folder, this.name);
|
200 | }
|
201 |
|
202 | FileInstrumenter.prototype._getOutputFile = function() {
|
203 | var folder = this.opts.outputPath;
|
204 | return path.resolve(folder, this.name);
|
205 | }
|
206 |
|
207 |
|
208 | module.exports = FileInstrumenter;
|