1 | const {
|
2 | join,
|
3 | dirname,
|
4 | } = require('path');
|
5 | const {
|
6 | readFileSync,
|
7 | } = require('fs');
|
8 | const {
|
9 | spawnSync,
|
10 | } = require('child_process');
|
11 | const compiler = require('vue-template-compiler');
|
12 | const transpile = require('vue-template-es2015-compiler');
|
13 | const postcss = require('postcss');
|
14 | const postcssModules = require('postcss-modules-sync').default;
|
15 |
|
16 | const COMPONENT_OPTIONS = '((module.exports.default || module.exports).options || module.exports.default || module.exports)';
|
17 |
|
18 | const defaultConfig = {
|
19 | transpileTemplates: true,
|
20 | sourceMaps: true,
|
21 | };
|
22 |
|
23 | let globalConfig = Object.assign({}, defaultConfig);
|
24 |
|
25 | let sourceMapSupport = false;
|
26 |
|
27 | function retrieveContent(
|
28 | filename,
|
29 | config
|
30 | ) {
|
31 | if (config.attrs.src) {
|
32 | const fullPath = join(dirname(filename), config.attrs.src);
|
33 | return readFileSync(fullPath, 'utf8');
|
34 | } else if (config.content) {
|
35 | return config.content;
|
36 | }
|
37 |
|
38 | return '';
|
39 | }
|
40 |
|
41 | function retrieveAndTranspileContent(
|
42 | filename,
|
43 | config,
|
44 | hook,
|
45 | noTranspileLangs,
|
46 | transpileSpecial
|
47 | ) {
|
48 | const content = retrieveContent(filename, config);
|
49 | const lang = (config.attrs.lang || '').toLowerCase();
|
50 |
|
51 | if (lang && noTranspileLangs.includes(lang) === false) {
|
52 | if (transpileSpecial == null) {
|
53 | return hook(lang, content);
|
54 | } else {
|
55 | return transpileSpecial(content, lang);
|
56 | }
|
57 | }
|
58 |
|
59 | return content;
|
60 | }
|
61 |
|
62 | function getScriptPart(
|
63 | filename,
|
64 | hook,
|
65 | script
|
66 | ) {
|
67 | if (!script) {
|
68 | return 'module.exports = { functional: true }';
|
69 | }
|
70 | return retrieveAndTranspileContent(
|
71 | filename,
|
72 | script,
|
73 | hook,
|
74 | [ 'js', 'javascript' ]
|
75 | );
|
76 | }
|
77 |
|
78 | function transpileTemplateSpecial(
|
79 | content,
|
80 | lang
|
81 | ) {
|
82 | const result = spawnSync(
|
83 | process.execPath,
|
84 | [
|
85 | join(__dirname, './renderTemplate.js'),
|
86 | lang,
|
87 | content,
|
88 | ],
|
89 | {
|
90 | encoding: 'utf-8',
|
91 | }
|
92 | );
|
93 |
|
94 | if (result.stderr) {
|
95 | throw new Error(result.stderr);
|
96 | }
|
97 |
|
98 | return result.stdout;
|
99 | }
|
100 |
|
101 | function getCompiledTemplate(
|
102 | filename,
|
103 | hook,
|
104 | template
|
105 | ) {
|
106 | if (!template) {
|
107 | return '';
|
108 | }
|
109 |
|
110 | const content = retrieveAndTranspileContent(
|
111 | filename,
|
112 | template,
|
113 | hook,
|
114 | [ 'html' ],
|
115 | globalConfig.transpileTemplates && transpileTemplateSpecial
|
116 | );
|
117 |
|
118 | const compiled = compiler.compile(content, { preserveWhitespace: false });
|
119 | const renderFn = `function(){ ${compiled.render} }`;
|
120 | const staticRenderFns = (compiled.staticRenderFns || [])
|
121 | .map((fn) => `function(){ ${fn} }`)
|
122 | .join(',');
|
123 |
|
124 | return [
|
125 | ';',
|
126 | transpile(`${COMPONENT_OPTIONS}.render=${renderFn};`),
|
127 | transpile(`${COMPONENT_OPTIONS}.staticRenderFns = [ ${staticRenderFns} ];`),
|
128 | `${COMPONENT_OPTIONS}.render._withStripped = true;`,
|
129 | ].join('\n');
|
130 | }
|
131 |
|
132 | function getCssModuleComputedProps(
|
133 | filename,
|
134 | hook,
|
135 | styles
|
136 | ) {
|
137 | if (!styles) {
|
138 | return '';
|
139 | }
|
140 |
|
141 | const computedProps = styles
|
142 | .filter((style) => style.module !== undefined && style.module !== false)
|
143 | .map((style) => {
|
144 | const moduleName = (typeof style.module) === 'string' ? style.module : '$style';
|
145 | const content = retrieveAndTranspileContent(
|
146 | filename,
|
147 | style,
|
148 | hook,
|
149 | [ 'css' ]
|
150 | );
|
151 |
|
152 | let cssClasses;
|
153 | postcss(postcssModules(
|
154 | {
|
155 | generateScopedName: moduleName + '-[local]',
|
156 | getJSON: json => { cssClasses = json; }
|
157 | })).process(content).css;
|
158 | const cssClassesStr = JSON.stringify(cssClasses);
|
159 |
|
160 | return `${COMPONENT_OPTIONS}.computed.${moduleName} = function(){ return ${cssClassesStr}; };`;
|
161 | });
|
162 |
|
163 | if (!computedProps.length) {
|
164 | return '';
|
165 | }
|
166 |
|
167 | return `${COMPONENT_OPTIONS}.computed = ${COMPONENT_OPTIONS}.computed || {}; ${computedProps.join(' ')}`;
|
168 | }
|
169 |
|
170 | function processCustomBlocks (
|
171 | filename,
|
172 | hook,
|
173 | customBlocks
|
174 | ) {
|
175 | if (!customBlocks)
|
176 | return '';
|
177 | return customBlocks.map(customBlock => {
|
178 | try {
|
179 | return hook('vue-block-' + customBlock.type, customBlock.content);
|
180 | } catch (err) {
|
181 | return '';
|
182 | }
|
183 | }).join('\n');
|
184 | }
|
185 |
|
186 | module.exports = ({ content, filename, hook }) => {
|
187 | const {
|
188 | template,
|
189 | script,
|
190 | styles,
|
191 | customBlocks,
|
192 | } = compiler.parseComponent(content, { pad: 'line' });
|
193 |
|
194 | if (globalConfig.sourceMaps && sourceMapSupport === false) {
|
195 | require('source-map-support').install({
|
196 | hookRequired: true,
|
197 | });
|
198 | sourceMapSupport = true;
|
199 | }
|
200 |
|
201 | const scriptPart = getScriptPart(
|
202 | filename,
|
203 | hook,
|
204 | script
|
205 | );
|
206 |
|
207 | const compiledTemplate = getCompiledTemplate(
|
208 | filename,
|
209 | hook,
|
210 | template
|
211 | );
|
212 |
|
213 | const cssModulesComputedProps = getCssModuleComputedProps(
|
214 | filename,
|
215 | hook,
|
216 | styles
|
217 | );
|
218 |
|
219 | const processedCustomBlocks = processCustomBlocks(
|
220 | filename,
|
221 | hook,
|
222 | customBlocks
|
223 | );
|
224 |
|
225 | const result = [
|
226 | scriptPart,
|
227 | compiledTemplate,
|
228 | cssModulesComputedProps,
|
229 | processedCustomBlocks,
|
230 | ].join('\n');
|
231 |
|
232 | return { content: result };
|
233 | };
|
234 |
|
235 | module.exports.configure = (config) => {
|
236 | globalConfig = Object.assign({}, defaultConfig, globalConfig, config);
|
237 | };
|
238 |
|
239 | module.exports.COMPONENT_OPTIONS = COMPONENT_OPTIONS;
|