UNPKG

4.18 kBJavaScriptView Raw
1/*
2 * files.js
3 * loads templates, configurations and other files from disk
4 *
5 */
6
7var fs = require('fs');
8var path = require('path');
9var util = require('racer/lib/util');
10var htmlUtil = require('html-util');
11var resolve = require('resolve');
12
13exports.loadViewsSync = loadViewsSync;
14exports.loadStylesSync = loadStylesSync;
15exports.cssCompiler = cssCompiler;
16exports.htmlCompiler = htmlCompiler;
17
18function loadViewsSync(app, sourceFilename, namespace) {
19 var views = [];
20 var files = [];
21 var resolved = resolve.sync(sourceFilename, {extensions: app.viewExtensions, packageFilter: deleteMain});
22 if (!resolved) {
23 throw new Error('View template file not found: ' + sourceFilename);
24 }
25
26 var file = fs.readFileSync(resolved, 'utf8');
27
28 var extension = path.extname(resolved);
29 var compiler = app.compilers[extension];
30 if (!compiler) {
31 throw new Error('Unable to find compiler for: ' + extension);
32 }
33
34 var htmlFile = compiler(file, resolved);
35
36 var parsed = parseViews(namespace, htmlFile, resolved, app.viewExtensions);
37 for (var i = 0, len = parsed.imports.length; i < len; i++) {
38 var item = parsed.imports[i];
39 var imported = loadViewsSync(app, item.filename, item.namespace);
40 views = views.concat(imported.views);
41 files = files.concat(imported.files);
42 }
43 return {
44 views: views.concat(parsed.views)
45 , files: files.concat(resolved)
46 };
47}
48
49function htmlCompiler(file, filename) {
50 return file;
51}
52
53function parseViews(namespace, file, filename, extensions) {
54 var imports = [];
55 var views = [];
56 var prefix = (namespace) ? namespace + ':' : '';
57
58 htmlUtil.parse(file + '\n', {
59 // Force view tags to be treated as raw tags,
60 // meaning their contents are not parsed as HTML
61 rawTags: /^(?:[^\s=\/!>]+:|style|script)$/i
62 , matchEnd: matchEnd
63 , start: onStart
64 , text: onText
65 });
66
67 function matchEnd(tagName) {
68 if (tagName.slice(-1) === ':') {
69 return /<\/?[^\s=\/!>]+:[\s>]/i;
70 }
71 return new RegExp('</' + tagName, 'i');
72 }
73
74 // These variables pass state from attributes in the start tag to the
75 // following view template text
76 var name, attrs;
77
78 function onStart(tag, tagName, tagAttrs) {
79 var lastChar = tagName.charAt(tagName.length - 1);
80 if (lastChar !== ':') {
81 throw new Error('Expected tag ending in colon (:) instead of ' + tag);
82 }
83 name = tagName.slice(0, -1);
84 attrs = tagAttrs;
85 if (name === 'import') {
86 var dir = path.dirname(filename);
87 var resolved = resolve.sync(attrs.src, {basedir: dir, extensions: extensions, packageFilter: deleteMain});
88 var extension = path.extname(resolved);
89 var importNamespace = (attrs.ns == null) ?
90 path.basename(attrs.src, extension) : attrs.ns;
91 imports.push({
92 filename: resolved
93 , namespace: (!importNamespace) ? namespace : prefix + importNamespace
94 });
95 }
96 }
97
98 function onText(text, isRawText) {
99 if (!name || name === 'import') return;
100 views.push({
101 name: prefix + name
102 , source: text
103 , options: attrs
104 , filename: filename
105 });
106 }
107
108 return {
109 imports: imports
110 , views: views
111 };
112}
113
114function loadStylesSync(app, sourceFilename, options) {
115 options || (options = {compress: util.isProduction});
116 var resolved = resolve.sync(sourceFilename, {extensions: app.styleExtensions, packageFilter: deleteMain});
117 if (!resolved) {
118 throw new Error('Style file not found: ' + sourceFilename);
119 }
120 var extension = path.extname(resolved);
121 var compiler = app.compilers[extension];
122 if (!compiler) {
123 throw new Error('Unable to find compiler for: ' + extension);
124 }
125 var file = fs.readFileSync(resolved, 'utf8');
126 return compiler(file, resolved, options);
127}
128
129function cssCompiler(file, filename, options) {
130 return {css: file, files: [filename]};
131}
132
133// Resolve will use a main path from a package.json if found. Main is the
134// entry point for javascript in a module, so this will mistakenly cause us to
135// load the JS file instead of a view or style file in some cases. This package
136// filter deletes the main property so that the normal file name lookup happens
137function deleteMain(package) {
138 delete package.main;
139}