UNPKG

8.59 kBJavaScriptView Raw
1var FS = require("fs");
2var Path = require("path");
3var Tree = require("./htmltree");
4var Util = require("./util");
5var Fatal = require("./fatal");
6var Source = require("./source");
7var Template = require("./template");
8var ParserHTML = require("./tlk-htmlparser");
9var CompilerCOM = require("./compiler-com");
10var CompilerHTML = require("./compiler-html2");
11
12
13module.exports = function(source, components, scopes, output) {
14 var ID = 0;
15 function setVar(key, val) {
16 scopes[scopes.length - 1][key] = val;
17 }
18 function getVar(key) {
19 var k, v;
20 for (k = scopes.length - 1; k >= 0; k--) {
21 v = scopes[k][key];
22 if (typeof v !== 'undefined') {
23 return v;
24 }
25 }
26 return '';
27 }
28 // Comnpilation options are set by the function `libs.compile(root, options)`.
29 var compilationOptions = {};
30
31 var prj = source.prj();
32
33 /**
34 * @return
35 * The path of the current HTML file, relative to the `src` path.
36 */
37 function dirname() {
38 var absoluteDirname = Path.dirname(source.getAbsoluteFilePath());
39 var absoluteSrcPath = source.prj().srcPath();
40 return absoluteDirname.substr(absoluteSrcPath.length);
41 }
42
43 var libs = {
44 Tree: Tree,
45 Template: Template,
46 // An HTML file can ask to compile another HTML file with this function.
47 compileHTML: function(src) {
48 CompilerHTML.compile(src, compilationOptions);
49 output.include[src] = 1;
50 },
51 /**
52 * The depth of `path` is the number of subfolders it defines. For
53 * example, `foo.js' defined no subfolder and it is of depth 0. But
54 * `foo/bar/file.html` has two levels of subfolders hence it is of depth
55 * 2.
56 */
57 getBackToRoot: function (path) {
58 // Counter for '/'.
59 var standardFolderSepCount = 0;
60 // Counter for '\' (windows folder separator).
61 var windowsFolderSepCount = 0;
62 // Loops index used for parsing chars of `path`and to add `../` to the result.
63 var i;
64 // Current char read from `path`.
65 var c;
66 // Counting folders' separators.
67 for (i = 0; i < path.length; i++) {
68 c = path.charAt(i);
69 if (c == '/') standardFolderSepCount++;
70 if (c == '\\') windowsFolderSepCount++;
71 }
72 var folderSepCount = Math.max(standardFolderSepCount, windowsFolderSepCount);
73 if (folderSepCount == 0) {
74 // There is no subfolder.
75 return '';
76 }
77
78 var result = '';
79 var folderSep = '/'; // windowsFolderSepCount > standardFolderSepCount ? '\\' : '/';
80 for (i = 0; i < folderSepCount; i++) {
81 result += '..' + folderSep;
82 }
83 return result;
84 },
85 fatal: function(msg, tree) {
86 if (typeof tree !== 'undefined' && typeof tree.pos === 'number') {
87 msg += "\n\n" + Fatal.extractCodeAtPos(source.read(), tree.pos);
88 }
89 Fatal.fire(msg, "Component");
90 },
91 warning: function(msg, filename, content, pos) {
92 filename = filename || source.name();
93 console.log("------------------------------------------------------------".yellow);
94 console.log("Warning in file ".yellow + filename.cyan);
95 console.log(msg.yellow);
96 console.log();
97 if (typeof content === 'string') {
98 // There is code to show.
99 console.log(Fatal.extractCodeAtPos(content, pos).yellow);
100 }
101 },
102 nextID: function() {
103 ID++;
104 return 'x-' + ID;
105 },
106 setVar: setVar,
107 getVar: getVar,
108 fileExists: function(relPath) {
109 var name = Path.join( Path.dirname( source.name() ), relPath );
110 var srcPath = source.prj().srcOrLibPath( name );
111 return srcPath;
112 },
113 filePath: function(relPath) {
114 var name = Path.join( Path.dirname( source.name() ), relPath );
115 var srcPath = source.prj().srcOrLibPath( name );
116 return srcPath;
117 },
118 /**
119 * @return
120 * The path of the current HTML file, relative to the `src` path.
121 */
122 dirname: dirname,
123 /**
124 * Return the absolute path of a path relative to the HTML file.
125 * @param path {string} - Path relative to the HTML file.
126 */
127 htmPath: function(path) {
128 return source.prj().srcPath(Path.join(dirname(), path));
129 },
130 readFileContent: function(relPath) {
131 try {
132 var name = Path.join( Path.dirname( source.name() ), relPath );
133 var srcPath = source.prj().srcOrLibPath( name );
134 if( !srcPath ) return "";
135 return FS.readFileSync( srcPath ).toString();
136 }
137 catch (ex) {
138 Fatal.bubble(ex, "readFileContent(\"" + relPath + "\")", "");
139 }
140 },
141 addDynamicModule: function( name, code ) {
142 output.dynamicModules['mod/' + name] =
143 "require('" + name + "', function(exports, module) {\n"
144 + code + "\});\n";
145 },
146 addDependency: function(dependency) {
147 output.dependencies[dependency] = 1;
148 },
149 addInnerCSS: function(contentCSS) {
150 output.innerCSS[contentCSS] = 1;
151 },
152 addInitJS: function(code) {
153 output.initJS[code] = 1;
154 },
155 addPostInitJS: function(code) {
156 output.postInitJS[code] = 1;
157 },
158 addInclude: function(src) {
159 output.include[src] = 1;
160 },
161 addResourceText: function(name, dst, txt) {
162 output.resource[name] = {dst: dst, txt: txt};
163 },
164 addResourceFile: function(name, dst, src) {
165 output.resource[name] = {dst: dst, src: src};
166 },
167 require: function(moduleName) {
168 var prefix = moduleName.substr(0, 4);
169 if (prefix != 'mod/' && prefix != 'cls/') {
170 // Add directory if missing.
171 moduleName = 'mod/' + moduleName;
172 }
173 output.require[moduleName] = 1;
174 },
175 parseHTML: ParserHTML.parse
176 };
177
178 // Project configuration.
179 var cfg = prj._config;
180 setVar('$filename', source.name());
181 setVar('$title', cfg.name);
182 setVar('$author', cfg.author);
183 setVar('$version', cfg.version);
184
185
186 function compileChildren(root) {
187 if (Array.isArray(root.children)) {
188 // We create a new children array to avoid nodes of type VOID.
189 var result = [];
190 root.children.forEach(function (child) {
191 libs.compile(child);
192 if (child.type == Tree.VOID || typeof child.type === 'undefined') {
193 result.push.apply( result, child.children );
194 } else {
195 result.push( child );
196 }
197 });
198 root.children = result;
199 }
200 }
201 libs.compileChildren = compileChildren;
202 libs.compile = function(root, options) {
203 compilationOptions = options;
204 if (root.type !== Tree.TAG) {
205 compileChildren(root);
206 return;
207 } else {
208 var component = CompilerCOM.getCompilerForTag(root.name);
209 if (component) {
210 if (component.$.css) {
211 libs.addInnerCSS(component.$.css);
212 }
213 if (component.$.res) {
214 // There is a resource folder.
215 var dstPath = source.name();
216 libs.addResourceFile(
217 Path.join('com', component.$.id),
218 Path.join('css', Path.dirname(dstPath), Path.join('com', component.$.id)),
219 component.$.res
220 );
221 }
222 try {
223 scopes.push({});
224 component.compile(root, libs);
225 scopes.pop();
226 }
227 catch (ex) {
228 Fatal.bubble(ex, "Tag: " + root.name + ", HTML file: " + source.name());
229 }
230 } else {
231 // This is a standard TAG. Let's loop on children.
232 compileChildren(root);
233 }
234 }
235 };
236 return libs;
237};