UNPKG

13.7 kBJavaScriptView Raw
1"use strict";
2var __importDefault = (this && this.__importDefault) || function (mod) {
3 return (mod && mod.__esModule) ? mod : { "default": mod };
4};
5Object.defineProperty(exports, "__esModule", { value: true });
6try {
7 require.resolve('@vue/compiler-sfc');
8}
9catch (e) {
10 throw new Error('rollup-plugin-vue requires @vue/compiler-sfc to be present in the dependency ' +
11 'tree.');
12}
13const compiler_sfc_1 = require("@vue/compiler-sfc");
14const debug_1 = __importDefault(require("debug"));
15const hash_sum_1 = __importDefault(require("hash-sum"));
16const path_1 = require("path");
17const querystring_1 = __importDefault(require("querystring"));
18const rollup_pluginutils_1 = require("rollup-pluginutils");
19const debug = debug_1.default('rollup-plugin-vue');
20const defaultOptions = {
21 include: /\.vue$/,
22 exclude: [],
23 target: 'browser',
24 exposeFilename: false,
25};
26function PluginVue(userOptions = {}) {
27 const options = {
28 ...defaultOptions,
29 ...userOptions,
30 };
31 const isServer = options.target === 'node';
32 const isProduction = process.env.NODE_ENV === 'production' || process.env.BUILD === 'production';
33 const rootContext = process.cwd();
34 const filter = rollup_pluginutils_1.createFilter(options.include, options.exclude);
35 return {
36 name: 'vue',
37 resolveId(id) {
38 const query = parseVuePartRequest(id);
39 if (query.vue) {
40 debug(`resolveId(${id})`);
41 return id;
42 }
43 return undefined;
44 },
45 load(id) {
46 const query = parseVuePartRequest(id);
47 if (query.vue) {
48 const descriptor = getDescriptor(query.filename);
49 const block = query.type === 'template'
50 ? descriptor.template
51 : query.type === 'script'
52 ? descriptor.script
53 : query.type === 'style'
54 ? descriptor.styles[query.index]
55 : query.type === 'custom'
56 ? descriptor.customBlocks[query.index]
57 : null;
58 if (block) {
59 return {
60 code: block.content,
61 map: normalizeSourceMap(block.map),
62 };
63 }
64 }
65 return undefined;
66 },
67 async transform(code, id) {
68 const query = parseVuePartRequest(id);
69 if (query.vue) {
70 const descriptor = getDescriptor(query.filename);
71 const hasScoped = descriptor.styles.some((s) => s.scoped);
72 if (query.type === 'template') {
73 debug(`transform(${id})`);
74 const block = descriptor.template;
75 const result = compiler_sfc_1.compileTemplate({
76 filename: query.filename,
77 source: code,
78 preprocessLang: block.lang,
79 preprocessCustomRequire: options.preprocessCustomRequire,
80 compiler: options.compiler,
81 ssr: isServer,
82 compilerOptions: {
83 ...options.compilerOptions,
84 scopeId: hasScoped ? `data-v-${query.id}` : undefined,
85 },
86 transformAssetUrls: options.transformAssetUrls,
87 });
88 if (result.errors.length) {
89 result.errors.forEach((error) => this.error(typeof error === 'string'
90 ? { id: query.filename, message: error }
91 : createRollupError(query.filename, error)));
92 return null;
93 }
94 if (result.tips.length) {
95 result.tips.forEach((tip) => this.warn({
96 id: query.filename,
97 message: tip,
98 }));
99 }
100 return {
101 code: result.code,
102 map: normalizeSourceMap(result.map),
103 };
104 }
105 else if (query.type === 'style') {
106 debug(`transform(${id})`);
107 const block = descriptor.styles[query.index];
108 const result = await compiler_sfc_1.compileStyleAsync({
109 filename: query.filename,
110 id: `data-v-${query.id}`,
111 source: block.content,
112 scoped: block.scoped,
113 modules: !!block.module,
114 modulesOptions: options.cssModulesOptions,
115 preprocessLang: options.preprocessStyles
116 ? block.lang
117 : undefined,
118 preprocessCustomRequire: options.preprocessCustomRequire,
119 });
120 if (result.errors.length) {
121 result.errors.forEach((error) => this.error({
122 id: query.filename,
123 message: error.message,
124 }));
125 return null;
126 }
127 if (query.module) {
128 return {
129 code: `export default ${_(result.modules)}`,
130 map: null,
131 };
132 }
133 else {
134 return {
135 code: result.code,
136 map: normalizeSourceMap(result.map),
137 };
138 }
139 }
140 return null;
141 }
142 else if (filter(id)) {
143 debug(`transform(${id})`);
144 const { descriptor, errors } = parseSFC(code, id, rootContext);
145 if (errors.length) {
146 errors.forEach((error) => this.error(createRollupError(id, error)));
147 return null;
148 }
149 // module id for scoped CSS & hot-reload
150 const output = transformVueSFC(code, id, descriptor, { rootContext, isProduction, isServer }, options);
151 debug('transient .vue file:', '\n' + output + '\n');
152 return {
153 code: output,
154 map: {
155 mappings: '',
156 },
157 };
158 }
159 else {
160 return null;
161 }
162 },
163 };
164}
165exports.default = PluginVue;
166function parseVuePartRequest(id) {
167 const [filename, query] = id.split('?', 2);
168 if (!query)
169 return { vue: false, filename };
170 const raw = querystring_1.default.parse(query);
171 if ('vue' in raw) {
172 return {
173 ...raw,
174 filename,
175 vue: true,
176 type: raw.type,
177 index: Number(raw.index),
178 scoped: 'scoped' in raw,
179 module: raw.module,
180 };
181 }
182 return { vue: false, filename };
183}
184const cache = new Map();
185function getDescriptor(id) {
186 if (cache.has(id)) {
187 return cache.get(id);
188 }
189 throw new Error(`${id} is not parsed it yet`);
190}
191function parseSFC(code, id, sourceRoot) {
192 const { descriptor, errors } = compiler_sfc_1.parse(code, {
193 sourceMap: true,
194 filename: id,
195 sourceRoot: sourceRoot,
196 pad: 'line',
197 });
198 cache.set(id, descriptor);
199 return { descriptor, errors };
200}
201function transformVueSFC(code, resourcePath, descriptor, { rootContext, isProduction, isServer, }, options) {
202 const shortFilePath = path_1.relative(rootContext, resourcePath)
203 .replace(/^(\.\.[\/\\])+/, '')
204 .replace(/\\/g, '/');
205 const id = hash_sum_1.default(isProduction ? shortFilePath + '\n' + code : shortFilePath);
206 // feature information
207 const hasScoped = descriptor.styles.some((s) => s.scoped);
208 const templateImport = getTemplateCode(descriptor, resourcePath, id, hasScoped, isServer);
209 const scriptImport = getScriptCode(descriptor, resourcePath);
210 const stylesCode = getStyleCode(descriptor, resourcePath, id, options.preprocessStyles);
211 const output = [
212 scriptImport,
213 templateImport,
214 stylesCode,
215 isServer ? `script.ssrRender = ssrRender` : `script.render = render`,
216 ];
217 if (hasScoped) {
218 output.push(`script.__scopeId = ${_(`data-v-${id}`)}`);
219 }
220 if (!isProduction) {
221 output.push(`script.__file = ${_(shortFilePath)}`);
222 }
223 else if (options.exposeFilename) {
224 output.push(`script.__file = ${_(path_1.basename(shortFilePath))}`);
225 }
226 output.push('export default script');
227 return output.join('\n');
228}
229function getTemplateCode(descriptor, resourcePath, id, hasScoped, isServer) {
230 let templateImport = `const render = () => {}`;
231 let templateRequest;
232 if (descriptor.template) {
233 const src = descriptor.template.src || resourcePath;
234 const idQuery = `&id=${id}`;
235 const scopedQuery = hasScoped ? `&scoped=true` : ``;
236 const attrsQuery = attrsToQuery(descriptor.template.attrs);
237 const query = `?vue&type=template${idQuery}${scopedQuery}${attrsQuery}`;
238 templateRequest = _(src + query);
239 templateImport = `import { ${isServer ? 'ssrRender' : 'render'} } from ${templateRequest}`;
240 }
241 return templateImport;
242}
243function getScriptCode(descriptor, resourcePath) {
244 let scriptImport = `const script = {}`;
245 if (descriptor.script) {
246 const src = descriptor.script.src || resourcePath;
247 const attrsQuery = attrsToQuery(descriptor.script.attrs, 'js');
248 const query = `?vue&type=script${attrsQuery}`;
249 const scriptRequest = _(src + query);
250 scriptImport =
251 `import script from ${scriptRequest}\n` + `export * from ${scriptRequest}`; // support named exports
252 }
253 return scriptImport;
254}
255function getStyleCode(descriptor, resourcePath, id, preprocessStyles) {
256 let stylesCode = ``;
257 let hasCSSModules = false;
258 if (descriptor.styles.length) {
259 descriptor.styles.forEach((style, i) => {
260 const src = style.src || resourcePath;
261 // do not include module in default query, since we use it to indicate
262 // that the module needs to export the modules json
263 const attrsQuery = attrsToQuery(style.attrs, 'css', preprocessStyles);
264 const attrsQueryWithoutModule = attrsQuery.replace(/&module(=true)?/, '');
265 // make sure to only pass id when necessary so that we don't inject
266 // duplicate tags when multiple components import the same css file
267 const idQuery = style.scoped ? `&id=${id}` : ``;
268 const query = `?vue&type=style&index=${i}${idQuery}`;
269 const styleRequest = src + query + attrsQuery;
270 const styleRequestWithoutModule = src + query + attrsQueryWithoutModule;
271 if (style.module) {
272 if (!hasCSSModules) {
273 stylesCode += `const cssModules = script.__cssModules = {}`;
274 hasCSSModules = true;
275 }
276 stylesCode += genCSSModulesCode(id, i, styleRequest, styleRequestWithoutModule, style.module);
277 }
278 else {
279 stylesCode += `\nimport ${_(styleRequest)}`;
280 }
281 // TODO SSR critical CSS collection
282 });
283 }
284 return stylesCode;
285}
286function createRollupError(id, error) {
287 return {
288 id,
289 plugin: 'vue',
290 pluginCode: String(error.code),
291 message: error.message,
292 frame: error.loc.source,
293 parserError: error,
294 loc: error.loc
295 ? {
296 file: id,
297 line: error.loc.start.line,
298 column: error.loc.start.column,
299 }
300 : undefined,
301 };
302}
303// these are built-in query parameters so should be ignored
304// if the user happen to add them as attrs
305const ignoreList = ['id', 'index', 'src', 'type'];
306function attrsToQuery(attrs, langFallback, forceLangFallback = false) {
307 let query = ``;
308 for (const name in attrs) {
309 const value = attrs[name];
310 if (!ignoreList.includes(name)) {
311 query += `&${querystring_1.default.escape(name)}${value ? `=${querystring_1.default.escape(String(value))}` : ``}`;
312 }
313 }
314 if (langFallback) {
315 query +=
316 `lang` in attrs
317 ? forceLangFallback
318 ? `.${langFallback}`
319 : ``
320 : `&lang.${langFallback}`;
321 }
322 return query;
323}
324function _(any) {
325 return JSON.stringify(any);
326}
327function normalizeSourceMap(map) {
328 if (!map)
329 return null;
330 return {
331 ...map,
332 version: Number(map.version),
333 mappings: typeof map.mappings === 'string' ? map.mappings : '',
334 };
335}
336function genCSSModulesCode(
337// @ts-ignore
338id, index, request, requestWithoutModule, moduleName) {
339 const styleVar = `style${index}`;
340 let code =
341 // first import the CSS for extraction
342 `\nimport ${_(requestWithoutModule)}` +
343 // then import the json file to expose to component...
344 `\nimport ${styleVar} from ${_(request + '.js')}`;
345 // inject variable
346 const name = typeof moduleName === 'string' ? moduleName : '$style';
347 code += `\ncssModules["${name}"] = ${styleVar}`;
348 return code;
349}
350// overwrite for cjs require('rollup-plugin-vue')() usage
351module.exports = PluginVue;