1 | const
|
2 | fs = require('fs'),
|
3 | path = require('path'),
|
4 | glob = require('glob'),
|
5 | sgUtil = require('../util');
|
6 |
|
7 | class Collector {
|
8 | constructor({conf, CollectorStore, CollectorPaths, TemplateEngine, DataLoader}) {
|
9 | this.conf = conf;
|
10 | this.CollectorStore = CollectorStore;
|
11 | this.CollectorPaths = CollectorPaths;
|
12 | this.TemplateEngine = TemplateEngine;
|
13 | this.DataLoader = DataLoader;
|
14 |
|
15 | this.copyExtend = conf.copyExtend;
|
16 | this.baseDir = conf.get('baseDir');
|
17 | this.viewerRootPath = conf.get('app.viewerRootPath');
|
18 | this.templateExt = conf.get('templateExt');
|
19 | this.htmlExt = conf.get('htmlExt');
|
20 | this.globOptions = conf.get('globOptions');
|
21 | this.logLevel = conf.get('logLevel', 0);
|
22 | this.warnOnMissingDataFile = this.logLevel > 2;
|
23 | this.globPattern = path.join('**', '**', `*.${this.templateExt}`);
|
24 | this.logLevel = conf.get('logLevel', 0);
|
25 | }
|
26 |
|
27 | touchFileSystem(sourceDir) {
|
28 | const {globPattern, globOptions} = this;
|
29 | return glob.sync(path.join(sourceDir, globPattern), globOptions);
|
30 | }
|
31 |
|
32 | collectMatchingSections(sections, collector) {
|
33 | sections
|
34 | .filter((sectionConfig) => sectionConfig.collector === collector)
|
35 | .map((sectionConfig) => this.collectSection.call(this, sectionConfig));
|
36 | }
|
37 |
|
38 | collectSection(sectionConfig) {
|
39 | const {
|
40 | baseDir, viewerRootPath, htmlExt, globPattern, warnOnMissingDataFile, CollectorStore,
|
41 | setFile, unsetFile, updateFile, touchFileSystem, initDataLoader
|
42 | } = this;
|
43 | const {title, source, dest, collector} = sectionConfig;
|
44 | const sourceDir = path.resolve(path.join(baseDir, source));
|
45 | const destDir = dest || source;
|
46 | const dataLoader = initDataLoader.call(this, sectionConfig);
|
47 | const section = {
|
48 | collector,
|
49 | title,
|
50 | destDir,
|
51 | sourceDir,
|
52 | dataLoader,
|
53 | globPattern,
|
54 | warnOnMissingDataFile,
|
55 | watchPattern: path.join(baseDir, source, '**', '*'),
|
56 | url: `/${viewerRootPath}/${destDir}`,
|
57 | route: `/${viewerRootPath}/${destDir}/*.${htmlExt}`,
|
58 | setFile: setFile.bind(this),
|
59 | unsetFile: unsetFile.bind(this),
|
60 | updateFile: updateFile.bind(this)
|
61 | };
|
62 |
|
63 | touchFileSystem
|
64 | .call(this, sourceDir)
|
65 | .map((filename) => {
|
66 | setFile.call(this, {filename, section})
|
67 | });
|
68 |
|
69 | CollectorStore.setSection(section);
|
70 |
|
71 | return section;
|
72 | }
|
73 |
|
74 | unsetFile(filename) {
|
75 | this.CollectorStore.unsetFile(filename);
|
76 | }
|
77 |
|
78 | setFile(fileOptions) {
|
79 | return this.CollectorStore.setFile(this.createFileObj(fileOptions));
|
80 | }
|
81 |
|
82 | updateFile(filename) {
|
83 | this.CollectorStore.updateFile(filename);
|
84 | }
|
85 |
|
86 | createFileObj({filename, section: {sourceDir, destDir, url: sectionUrl, dataLoader, collector, warnOnMissingDataFile}}) {
|
87 | const {
|
88 | constructor: {name: instance}, logLevel,
|
89 | getSaveHtmlFunction, getSaveLocalsFunction, getComponentName,
|
90 | getCssFiles, getRenderHookFunction, getCopySourceFunction,
|
91 | isExcluded, CollectorPaths, TemplateEngine
|
92 | } = this;
|
93 |
|
94 | CollectorPaths.set(filename, sourceDir, destDir);
|
95 |
|
96 | const url = CollectorPaths.resolveUrl(filename);
|
97 | const sectionPath = path.relative(sectionUrl, path.dirname(url));
|
98 | const componentName = getComponentName.call(this, sourceDir, filename);
|
99 |
|
100 | return {
|
101 | filename,
|
102 | collector,
|
103 | destDir,
|
104 | componentName,
|
105 | instance,
|
106 | sectionPath,
|
107 | url,
|
108 | warnOnMissingDataFile,
|
109 | timestamp: Date.now(),
|
110 | childComponents: [],
|
111 | parentComponents: [],
|
112 | exclude: isExcluded(filename, componentName),
|
113 | extension: sgUtil.getFileExtension(filename),
|
114 | cssFiles: getCssFiles.call(this),
|
115 | saveHtml: getSaveHtmlFunction.call(this, filename),
|
116 | saveLocals: getSaveLocalsFunction.call(this, filename),
|
117 | renderHook: getRenderHookFunction.call(this),
|
118 | copySource: getCopySourceFunction.call(this, filename, componentName),
|
119 | get template() {
|
120 | if (!this._template || this._template.timestamp !== this.timestamp) {
|
121 |
|
122 | if (!fs.existsSync(this.filename)) {
|
123 | return '';
|
124 | }
|
125 |
|
126 | this._template = {
|
127 | content: sgUtil.readFileContents(this.filename),
|
128 | timestamp: this.timestamp
|
129 | };
|
130 | }
|
131 | return this._template.content;
|
132 | },
|
133 | get orderValue() {
|
134 | return this.data.orderValue || sectionPath;
|
135 | },
|
136 | get data() {
|
137 | if (!this._data || this._data.timestamp !== this.timestamp) {
|
138 | this._data = dataLoader ? dataLoader.getData(this) : {};
|
139 | this._data.title = this.title;
|
140 | this._data.pageTitle = this.pageTitle || this._data.title;
|
141 | this._data.instance = this.instance;
|
142 | this._data.collector = this.collector;
|
143 | this._data.timestamp = this.timestamp;
|
144 |
|
145 | if (logLevel > 1) {
|
146 | sgUtil.log(`Data: \u001b[1m${this.componentName}\u001b[22m generated. (${this.timestamp})`, 'info');
|
147 | }
|
148 | }
|
149 | else if (logLevel > 1) {
|
150 | sgUtil.log(`Data: \u001b[1m${this.componentName}\u001b[22m use cached. (${this.timestamp})`);
|
151 | }
|
152 | return this._data;
|
153 | },
|
154 | get render() {
|
155 | return TemplateEngine.render(this);
|
156 | },
|
157 | get title() {
|
158 | if (!this._title || !this._data || this._data.timestamp !== this.timestamp) {
|
159 | const {title = sgUtil.getTitleFromFilename(this.filename)} =
|
160 | sgUtil.readRelatedData(this.filename, this.warnOnMissingDataFile);
|
161 | this._title = title;
|
162 | }
|
163 | return this._title;
|
164 | }
|
165 | };
|
166 | }
|
167 |
|
168 | readRelatedData(filename) {
|
169 | return filename ? sgUtil.readJson5File(filename) : {};
|
170 | }
|
171 |
|
172 | initDataLoader(sectionConfig) {
|
173 | return new this.DataLoader(this.copyExtend(sectionConfig), this.warnOnMissingDataFile);
|
174 | }
|
175 |
|
176 | getSaveHtmlFunction(filename) {
|
177 | filename = this.CollectorPaths.resolveDestHtml(filename);
|
178 | return ({html}) => sgUtil.writeFile(filename, html);
|
179 | }
|
180 |
|
181 | getSaveLocalsFunction(filename) {
|
182 | filename = this.CollectorPaths.resolveDestData(filename);
|
183 | return ({locals}) => sgUtil.writeJsonFile(filename, locals);
|
184 | }
|
185 |
|
186 | getRenderHookFunction() {
|
187 | return ({data: {locals = {}}}, source) => ({source, locals});
|
188 | }
|
189 |
|
190 | getCopySourceFunction(_filename, _componentName) {
|
191 | return (filename = _filename, componentName = _componentName) => {
|
192 | const dest = this.CollectorPaths.resolveTemplateSourceDest(componentName);
|
193 | sgUtil.copyFile(filename, dest, () => {});
|
194 | };
|
195 | }
|
196 |
|
197 | getCssFiles() {
|
198 | return [`/${this.viewerRootPath}/css/app.css`];
|
199 | }
|
200 |
|
201 | getGetSectionDataFunction() {
|
202 | const {title, instance, cssFiles, sectionFiles} = this;
|
203 | return {title, instance, app: {cssFiles}, locals: {files: sectionFiles}};
|
204 | }
|
205 |
|
206 | getComponentName(sourceDir, filename) {
|
207 | const componentType = path.basename(sourceDir);
|
208 | const componentName = path.basename(sgUtil.removeFileExtension(filename)).replace(/_/g, '');
|
209 |
|
210 | return `${componentType}-${componentName}`;
|
211 | }
|
212 |
|
213 | isExcluded(filename, componentName) {
|
214 | return path.basename(filename).substr(0, 1) === '_' || sgUtil.getFileExtension(componentName) !== '';
|
215 | }
|
216 | }
|
217 |
|
218 | module.exports = Collector;
|