UNPKG

6.92 kBJavaScriptView Raw
1"use strict";
2
3/**
4 * Provides the class `Source` used to add __tags__ on source files.
5 */
6module.exports = Source;
7
8
9/**
10 * @module source
11 */
12const
13 FS = require( "fs" ),
14 Path = require( "path" ),
15 Util = require( "./util" ),
16 Fatal = require( "./fatal" );
17
18/**
19 * @class Source
20 * @param {Project} prj {@link project~Project}
21 * @param {string} _file path to the source file, relative to the source path.
22 * @see {@link project~Project}
23 */
24function Source( prj, _file ) {
25 if ( typeof prj === 'string' ) {
26 throw Error( "[Source()] First argument must be an instance of class Project!" );
27 }
28 const
29 srcDir = prj.srcOrLibPath(),
30 file = removeSrcDirPrefixFromFile( srcDir, _file );
31
32 this._prj = prj;
33 this._name = file;
34 this._tmpFile = prj.tmpPath( file );
35 if ( !FS.existsSync( this._tmpFile ) ) {
36 prj.mkdir( Path.dirname( this._tmpFile ) );
37 }
38 this._absPath = prj.srcOrLibPath( file );
39 this._tags = null;
40}
41
42/**
43 * @returns {boolean} - If the source file exists or not.
44 */
45Source.prototype.exists = function exists() {
46 return this._absPath ? FS.existsSync( this._absPath ) : false;
47};
48
49Source.prototype.clone = function ( newExtension ) {
50 return new Source( this._prj, this.name( newExtension ) );
51};
52
53/**
54 * @return name of the file. It can be `index.html` or `cls/wtag.Button.js`.
55 */
56Source.prototype.name = function ( newExtension ) {
57 if ( typeof newExtension === 'undefined' ) return this._name;
58 var pos = this._name.lastIndexOf( '.' );
59 return this._name.substr( 0, pos + 1 ) + newExtension;
60};
61
62/**
63 * @return the project ob
64 */
65Source.prototype.prj = function () {
66 return this._prj;
67};
68
69/**
70 * @return an instance of Source based on the same project.
71 */
72Source.prototype.create = function ( file ) {
73 return new Source( this._prj, file );
74};
75
76/**
77 * @return Text content of the source.
78 */
79Source.prototype.read = function () {
80 if ( typeof this._absPath !== 'string' ) {
81 this._prj.fatal( "[Source.read] File not found: \"" + this._name + "\"!" );
82 }
83 return FS.readFileSync( this._absPath ).toString();
84};
85
86/**
87 * @return Text content of the source.
88 */
89Source.prototype.write = function ( content ) {
90 this._absPath = this._prj.srcPath( this.name() );
91 return FS.writeFileSync( this._absPath, content, { encoding: "utf8" } );
92};
93
94/**
95 * @returns {boolean} `true` if the tags' file exists and is more recent than the source file.
96 */
97Source.prototype.isUptodate = function () {
98 const
99 path = this.getAbsoluteFilePath(),
100 pathTmp = this._tmpFile,
101 pathJs = Util.replaceExtension( path, ".js" ),
102 pathXjs = Util.replaceExtension( path, ".xjs" ),
103 pathIni = Util.replaceExtension( path, ".ini" ),
104 pathDep = Util.replaceExtension( path, ".dep" ),
105 pathCss = Util.replaceExtension( path, ".css" ),
106 inputs = [ pathJs, pathXjs, pathIni, pathDep, pathCss ],
107 watch = this.tag( 'watch' ) || [];
108 inputs.push( ...watch );
109 return !Util.isNewerThan( inputs, pathTmp );
110};
111
112/**
113 * @return Last modification time of the source.
114 */
115Source.prototype.modificationTime = function () {
116 if ( !FS.existsSync( this._tmpFile ) ) {
117 var statSrc = FS.statSync( this._absPath );
118 return statSrc.mtime;
119 }
120 var statTmp = FS.statSync( this._tmpFile );
121 return statTmp.mtime;
122};
123
124/**
125 * Store tags on disk and mark the source file as _uptodate_.
126 */
127Source.prototype.save = function () {
128 FS.writeFileSync( this._tmpFile, JSON.stringify( this._tags || {} ) );
129};
130
131/**
132 * @return Absolute path of the source file. It can be in the project
133 * `src/` file or in the ToloFrameWork `lib/` path. If it is nowhere,
134 * return _null_.
135 */
136Source.prototype.getAbsoluteFilePath = function () {
137 return this._absPath;
138};
139
140Source.prototype.getPathRelativeToSource = function ( relPath ) {
141 var dir = Path.dirname( this._absPath );
142 if ( typeof relPath !== 'string' ) {
143 Fatal.fire(
144 'Argument `relPath` of function `getPathRelativeToSource()` must be a string!\n' +
145 'But actual value is of type `' + ( typeof relPath ) + '`.'
146 );
147 }
148 return Path.join( dir, relPath );
149};
150
151/**
152 * Get/Set tags.
153 * Don't forget to call method `save()` to store tags on disk and mark the source file has _uptodate_.
154 * @param {string} name name of the tag.
155 * @param value (optional) if defined, the value to set.
156 * @return If `value` is _undefined_, return the tag's value.
157 */
158Source.prototype.tag = function ( name, value ) {
159 if ( !this._tags ) {
160 if ( FS.existsSync( this._tmpFile ) ) {
161 try {
162 this._tags = JSON.parse( FS.readFileSync( this._tmpFile ).toString() );
163 } catch ( ex ) {
164 this._tags = {};
165 }
166 } else {
167 this._tags = {};
168 }
169 }
170 if ( typeof value === 'undefined' ) {
171 return this._tags[ name ];
172 }
173 if ( value === null ) {
174 delete this._tags[ name ];
175 } else {
176 this._tags[ name ] = value;
177 }
178};
179
180/**
181 * Resources are stored in a directory with the source's name without
182 * extendion. For instance, if the source is "tunnel.css", resources
183 * are stored in directory "tunnel/" in the same folder.
184 * @return Array of resources relative to the source's folder.
185 */
186Source.prototype.listResources = function () {
187 var dir = this.getAbsoluteFilePath();
188 var pos = dir.lastIndexOf( '.' );
189 if ( pos > -1 ) {
190 // Removing extension.
191 dir = dir.substr( 0, pos );
192 }
193 var root = Path.dirname( dir );
194 var listFiles = function ( path ) {
195 return FS.readdirSync( path ).map(
196 function ( x ) { return Path.join( path, x ); }
197 );
198 };
199 if ( false == FS.existsSync( dir ) ) {
200 return [];
201 }
202 var output = [];
203 var fringe = listFiles( dir );
204 while ( fringe.length > 0 ) {
205 var file = fringe.pop();
206 if ( false == FS.existsSync( file ) ) continue;
207 var stat = FS.statSync( file );
208 if ( stat.isDirectory() ) {
209 fringe = listFiles( file ).concat( fringe );
210 } else {
211 output.push( [ file.substr( root.length + 1 ), file ] );
212 }
213 }
214 return output;
215};
216
217/**
218 * If present, remove the `srcDir` prefix of `file`.
219 *
220 * @param {[type]} srcDir [description]
221 * @param {[type]} file [description]
222 * @returns {[type]} [description]
223 *
224 * @example
225 * const f = removeSrcDirPrefixFromFile;
226 * f("/src/", "/myfile.cpp") === "/myfile.cpp";
227 * f("/src/", "/src/myfile2.cpp") === "myfile2.cpp";
228 */
229function removeSrcDirPrefixFromFile( srcDir, file ) {
230 if ( typeof srcDir !== 'string' ) return file;
231 if ( typeof file !== 'string' ) return file;
232
233 if ( file.substr( 0, srcDir.length ) !== srcDir ) {
234 return file;
235 }
236 return file.substr( srcDir.length );
237}