UNPKG

4.33 kBJavaScriptView Raw
1"use strict";
2
3const
4Fs = require("fs"),
5Path = require("path"),
6Util = require("./util");
7
8const EXTENSIONS = ['js', 'xjs', 'css', 'ini', 'dep', 'vert', 'frag', 'wrk'];
9
10
11/**
12 * A module is something you can `require()`.
13 * * `mymodule.js`
14 * * `mymodule.xjs`
15 * * `mymodule.css`
16 * * `mymodule.ini`
17 * * `mymodule.dep`
18 * * `mymodule.vert`
19 * * `mymodule.frag`
20 * * `mymodule.wrk`
21 */
22class Module {
23 /**
24 * @param {string} _moduleName - Name of the module (with dots).
25 * @param {array} _srcPath - Array of absolute pathes where to look for sources.
26 */
27 constructor(_moduleName, _srcPath) {
28 const
29 moduleName = typeof _moduleName === 'string' && _moduleName.startsWith("mod/")
30 ? _moduleName.substr( 4 ) : _moduleName,
31 srcPath = Array.isArray(_srcPath) ? _srcPath : [_srcPath],
32 candidates = getCandidates(moduleName, srcPath),
33 candidate = getFirstViableCandidate(candidates),
34 properties = { name: moduleName };
35 for( const extension of EXTENSIONS) {
36 properties[`path${Util.capitalize( extension )}`] = `${candidate}.${extension}`;
37 properties[`exists${Util.capitalize( extension )}`] =
38 () => Fs.existsSync( `${candidate}.${extension}` );
39 }
40 properties.lastModificationTime = () => {
41 let time = 0;
42 for( const extension of EXTENSIONS) {
43 const filename = `${candidate}.${extension}`;
44 if( !Fs.exists(filename) ) continue;
45 const stat = FS.statSync( filename );
46 time = Math.max( time, stat.mtime.getTime() );
47 }
48 return time;
49 };
50 Util.readonly(this, properties);
51 }
52}
53
54
55/**
56 * List all possible absolute pathes for the given module.
57 *
58 * @param {string} moduleName - Name of the module (with dots).
59 * @param {array} srcPath - Array of absolute pathes where to look for sources.
60 * Module's files lie in the subdirectory `mod/` of a source directory.
61 * @returns {array} Array of absolute module pathes in order of preferrence.
62 * Because a module can be made of several different files with the same basename,
63 * These pathes have no file extension.
64 */
65function getCandidates(moduleName, srcPath) {
66 const
67 candidates = [],
68 moduleNames = getSynonyms(moduleName);
69
70 for( const path of srcPath) {
71 for( const name of moduleNames) {
72 candidates.push( Path.resolve( path, "mod", name) );
73 }
74 }
75 return candidates;
76}
77
78
79/**
80 * Names with dots can be found in subfolder.
81 * For instance a module named "tfw.view.textbox" can have its javascript code
82 * in "tfw/view/textbox.js" or in "tfw.view.textbox.js".
83 * The first syntax is prefered.
84 *
85 * @param {string} moduleName - Name of the module with dots.
86 * @returns {array} One or two synonyms for this module name.
87 */
88function getSynonyms(moduleName) {
89 const preferredName = moduleName.split(".").join("/");
90 if( preferredName === moduleName) {
91 return [moduleName];
92 }
93 return [preferredName, moduleName];
94}
95
96
97/**
98 * A candidate is viable if the `js` or the `xjs` file exists.
99 *
100 * @param {array} candidates - Pathes of module files (without extension).
101 * @returns {string} The first candidate if no module has been found.
102 */
103function getFirstViableCandidate(candidates) {
104 for( const candidate of candidates) {
105 const fileJS = `${candidate}.js`;
106 if( Fs.existsSync(fileJS) ) return candidate;
107 const fileXJS = `${candidate}.xjs`;
108 if( Fs.existsSync(fileXJS) ) return candidate;
109 }
110
111 return candidates[0];
112}
113
114
115/**
116 * Create a Module object from a filename.
117 * If this filename is not from an existing Module, return `null`.
118 *
119 * @param {string} filename - Name of the module (with dots).
120 * @param {array} _srcPath - Array of absolute pathes where to look for sources.
121 * @return {Module} Or `null` if no matching module.
122 */
123Module.fromFilename = function(filename, _srcPath) {
124 const
125 srcPath = Array.isArray(_srcPath) ? _srcPath : [_srcPath];
126
127 for( const path of srcPath) {
128 const absPath = Path.resolve( path );
129 const relative = Path.relative( absPath, path );
130 console.info("[module] path, relative=", path, relative);
131 }
132
133 return null;
134};
135
136
137module.exports = Module;