1 | const querystring = require('querystring')
|
2 | const loaderUtils = require('loader-utils')
|
3 | const normalize = require('./utils/normalize')
|
4 | const tryRequire = require('./utils/try-require')
|
5 | const styleCompilerPath = normalize.lib('style-compiler/index')
|
6 | const templateCompilerPath = normalize.lib('template-compiler/index')
|
7 | const jsonCompilerPath = normalize.lib('json-compiler/index')
|
8 | const templatePreprocessorPath = normalize.lib('template-compiler/preprocessor')
|
9 | const wxsLoaderPath = normalize.lib('wxs/wxs-loader')
|
10 | const wxmlLoaderPath = normalize.lib('wxml/wxml-loader')
|
11 | const wxssLoaderPath = normalize.lib('wxss/loader')
|
12 | const config = require('./config')
|
13 | const selectorPath = normalize.lib('selector')
|
14 | const extractorPath = normalize.lib('extractor')
|
15 | const addQuery = require('./utils/add-query')
|
16 |
|
17 |
|
18 | const hasBabel = !!tryRequire('babel-loader')
|
19 |
|
20 | const rewriterInjectRE = /\b(css(?:-loader)?(?:\?[^!]+)?)(?:!|$)/
|
21 |
|
22 | const defaultLang = {
|
23 | template: 'html',
|
24 | styles: 'css',
|
25 | script: 'js',
|
26 | json: 'json',
|
27 | wxs: 'wxs'
|
28 | }
|
29 |
|
30 | const postcssExtensions = [
|
31 | 'postcss', 'pcss', 'sugarss', 'sss'
|
32 | ]
|
33 |
|
34 | function getRawRequest ({ resource, loaderIndex, loaders }, excludedPreLoaders = /eslint-loader/) {
|
35 | return loaderUtils.getRemainingRequest({
|
36 | resource: resource,
|
37 | loaderIndex: loaderIndex,
|
38 | loaders: loaders.filter(loader => !excludedPreLoaders.test(loader.path))
|
39 | })
|
40 | }
|
41 |
|
42 |
|
43 |
|
44 |
|
45 | function ensureLoader (lang) {
|
46 | return lang
|
47 | .split('!')
|
48 | .map(loader =>
|
49 | loader.replace(
|
50 | /^([\w-]+)(\?.*)?/,
|
51 | (_, name, query) =>
|
52 | (/-loader$/.test(name) ? name : name + '-loader') + (query || '')
|
53 | )
|
54 | )
|
55 | .join('!')
|
56 | }
|
57 |
|
58 | function ensureBang (loader) {
|
59 | if (loader && loader.charAt(loader.length - 1) !== '!') {
|
60 | return loader + '!'
|
61 | } else {
|
62 | return loader
|
63 | }
|
64 | }
|
65 |
|
66 | function resolveLoaders (options, moduleId, isProduction, hasScoped, hasComment, usingComponents, needCssSourceMap, projectRoot) {
|
67 | let cssLoaderOptions = ''
|
68 | let wxmlLoaderOptions = ''
|
69 | let jsonCompilerOptions = ''
|
70 | if (needCssSourceMap) {
|
71 | cssLoaderOptions += '?sourceMap'
|
72 | }
|
73 |
|
74 | wxmlLoaderOptions += '?root=' + projectRoot
|
75 | jsonCompilerOptions += '?root=' + projectRoot
|
76 |
|
77 |
|
78 | cssLoaderOptions += (cssLoaderOptions ? '&' : '?') + 'root=' + projectRoot + '&importLoaders=1&extract=true'
|
79 |
|
80 | const defaultLoaders = {
|
81 | html: wxmlLoaderPath + wxmlLoaderOptions,
|
82 | css: getCSSLoaderString(),
|
83 | js: hasBabel ? 'babel-loader' : '',
|
84 | json: jsonCompilerPath + jsonCompilerOptions,
|
85 | wxs: wxsLoaderPath
|
86 | }
|
87 |
|
88 | function getCSSLoaderString (lang) {
|
89 | const langLoader = lang ? ensureBang(ensureLoader(lang)) : ''
|
90 | return ensureBang('css-loader' + cssLoaderOptions) + langLoader
|
91 | }
|
92 |
|
93 | return {
|
94 | defaultLoaders,
|
95 | getCSSLoaderString,
|
96 | loaders: Object.assign({}, defaultLoaders, options.loaders),
|
97 | preLoaders: options.preLoaders || {},
|
98 | postLoaders: options.postLoaders || {}
|
99 | }
|
100 | }
|
101 |
|
102 | module.exports = function createHelpers (loaderContext, options, moduleId, isProduction, hasScoped, hasComment, usingComponents, needCssSourceMap, srcMode, isNative, projectRoot) {
|
103 | const rawRequest = getRawRequest(loaderContext, options.excludedPreLoaders)
|
104 | const {
|
105 | defaultLoaders,
|
106 | getCSSLoaderString,
|
107 | loaders,
|
108 | preLoaders,
|
109 | postLoaders
|
110 | } = resolveLoaders(
|
111 | options,
|
112 | moduleId,
|
113 | isProduction,
|
114 | hasScoped,
|
115 | hasComment,
|
116 | usingComponents,
|
117 | needCssSourceMap,
|
118 | projectRoot
|
119 | )
|
120 |
|
121 | function getRequire (type, part, index, scoped) {
|
122 | return 'require(' + getRequestString(type, part, index, scoped) + ')'
|
123 | }
|
124 |
|
125 | function getImport (type, part, index, scoped) {
|
126 | return (
|
127 | 'import __' + type + '__ from ' +
|
128 | getRequestString(type, part, index, scoped)
|
129 | )
|
130 | }
|
131 |
|
132 | function getNamedExports (type, part, index, scoped) {
|
133 | return (
|
134 | 'export * from ' +
|
135 | getRequestString(type, part, index, scoped)
|
136 | )
|
137 | }
|
138 |
|
139 | function processQuery (request, mode, type) {
|
140 | let addQueryObj = {}
|
141 | let removeKeys
|
142 | if (mode) {
|
143 | addQueryObj.mode = mode
|
144 | }
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 | return addQuery(request, addQueryObj, removeKeys)
|
151 | }
|
152 |
|
153 | function getRequestString (type, part, index = 0, scoped) {
|
154 | return loaderUtils.stringifyRequest(
|
155 | loaderContext,
|
156 |
|
157 | '!!' +
|
158 |
|
159 | getLoaderString(type, part, index, scoped) +
|
160 |
|
161 | getSelectorString(type, index) +
|
162 |
|
163 | processQuery(rawRequest, part.mode, type)
|
164 | )
|
165 | }
|
166 |
|
167 | function getRequireForSrc (type, impt, index, scoped, prefix, withIssuer) {
|
168 | return 'require(' + getSrcRequestString(type, impt, index, scoped, prefix, withIssuer) + ')'
|
169 | }
|
170 |
|
171 | function getImportForSrc (type, impt, index, scoped, prefix) {
|
172 | return (
|
173 | 'import __' + type + '__ from ' +
|
174 | getSrcRequestString(type, impt, index, scoped, prefix)
|
175 | )
|
176 | }
|
177 |
|
178 | function getNamedExportsForSrc (type, impt, index, scoped, prefix) {
|
179 | return (
|
180 | 'export * from ' +
|
181 | getSrcRequestString(type, impt, index, scoped, prefix)
|
182 | )
|
183 | }
|
184 |
|
185 | function getSrcRequestString (type, impt, index = 0, scoped, prefix = '!', withIssuer) {
|
186 | let loaderString = type === 'script' ? '' : prefix + getLoaderString(type, impt, index, scoped, withIssuer)
|
187 | let src = impt.src
|
188 | return loaderUtils.stringifyRequest(
|
189 | loaderContext,
|
190 | loaderString + processQuery(src, impt.mode, type)
|
191 | )
|
192 | }
|
193 |
|
194 | function addCssModulesToLoader (loader, part, index) {
|
195 | if (!part.module) return loader
|
196 | const option = options.cssModules || {}
|
197 | const DEFAULT_OPTIONS = {
|
198 | modules: true
|
199 | }
|
200 | const OPTIONS = {
|
201 | localIdentName: '[hash:base64]'
|
202 | }
|
203 | return loader.replace(/((?:^|!)css(?:-loader)?)(\?[^!]*)?/, (m, $1, $2) => {
|
204 |
|
205 |
|
206 | const query = loaderUtils.parseQuery($2 || '?')
|
207 | Object.assign(query, OPTIONS, option, DEFAULT_OPTIONS)
|
208 | if (index !== -1) {
|
209 | query.localIdentName += '_' + index
|
210 | }
|
211 | return $1 + '?' + JSON.stringify(query)
|
212 | })
|
213 | }
|
214 |
|
215 | function buildCustomBlockLoaderString (attrs) {
|
216 | const noSrcAttrs = Object.assign({}, attrs)
|
217 | delete noSrcAttrs.src
|
218 | const qs = querystring.stringify(noSrcAttrs)
|
219 | return qs ? '?' + qs : qs
|
220 | }
|
221 |
|
222 |
|
223 | function stringifyLoaders (loaders) {
|
224 | return loaders
|
225 | .map(
|
226 | obj =>
|
227 | obj && typeof obj === 'object' && typeof obj.loader === 'string'
|
228 | ? obj.loader +
|
229 | (obj.options ? '?' + JSON.stringify(obj.options) : '')
|
230 | : obj
|
231 | )
|
232 | .join('!')
|
233 | }
|
234 |
|
235 | function getLoaderString (type, part, index, scoped, withIssuer) {
|
236 | let loader = getRawLoaderString(type, part, index, scoped)
|
237 | const lang = getLangString(type, part)
|
238 | if (type !== 'script' && type !== 'wxs') {
|
239 | loader = getExtractorString(type, index, withIssuer) + loader
|
240 | }
|
241 | if (preLoaders[lang]) {
|
242 | loader = loader + ensureBang(preLoaders[lang])
|
243 | }
|
244 | if (postLoaders[lang]) {
|
245 | loader = ensureBang(postLoaders[lang]) + loader
|
246 | }
|
247 | return loader
|
248 | }
|
249 |
|
250 | function getLangString (type, { lang }) {
|
251 | if (type === 'script' || type === 'template' || type === 'styles') {
|
252 | return lang || defaultLang[type]
|
253 | } else {
|
254 | return type
|
255 | }
|
256 | }
|
257 |
|
258 | function replaceCssLoader (rawLoader) {
|
259 | return rawLoader.replace(/css(?:-loader)?/, wxssLoaderPath)
|
260 | }
|
261 |
|
262 | function getRawLoaderString (type, part, index, scoped) {
|
263 | let lang = (part.lang && part.lang !== config[srcMode].typeExtMap.template.slice(1)) ? part.lang : defaultLang[type]
|
264 |
|
265 | let styleCompiler = ''
|
266 | if (type === 'styles') {
|
267 |
|
268 | styleCompiler = styleCompilerPath + '?' +
|
269 | JSON.stringify({
|
270 | moduleId,
|
271 | scoped: !!scoped,
|
272 | sourceMap: needCssSourceMap,
|
273 | transRpx: options.transRpx
|
274 | })
|
275 |
|
276 | if (!loaders[lang]) {
|
277 | if (postcssExtensions.indexOf(lang) !== -1) {
|
278 | lang = 'css'
|
279 | } else if (lang === 'sass') {
|
280 | lang = `sass?${JSON.stringify({
|
281 | sassOptions: {
|
282 | indentedSyntax: true
|
283 | }
|
284 | })}`
|
285 | } else if (lang === 'scss') {
|
286 | lang = 'sass'
|
287 | }
|
288 | }
|
289 | }
|
290 |
|
291 | let templateCompiler = ''
|
292 |
|
293 | if (type === 'template') {
|
294 | const templateCompilerOptions = {
|
295 | usingComponents,
|
296 | hasScoped,
|
297 | hasComment,
|
298 | isNative,
|
299 | moduleId,
|
300 | root: projectRoot
|
301 | }
|
302 | templateCompiler = templateCompilerPath + '?' + JSON.stringify(templateCompilerOptions)
|
303 | }
|
304 |
|
305 | let loader = type === 'styles'
|
306 | ? loaders[lang] || getCSSLoaderString(lang)
|
307 | : loaders[lang]
|
308 |
|
309 | if (loader != null) {
|
310 | if (Array.isArray(loader)) {
|
311 | loader = stringifyLoaders(loader)
|
312 | } else if (typeof loader === 'object') {
|
313 | loader = stringifyLoaders([loader])
|
314 | }
|
315 | if (type === 'styles') {
|
316 |
|
317 | loader = addCssModulesToLoader(loader, part, index)
|
318 |
|
319 | if (rewriterInjectRE.test(loader)) {
|
320 | loader = loader.replace(
|
321 | rewriterInjectRE,
|
322 | (m, $1) => ensureBang($1) + ensureBang(styleCompiler)
|
323 | )
|
324 | } else {
|
325 | loader = ensureBang(loader) + ensureBang(styleCompiler)
|
326 | }
|
327 | loader = replaceCssLoader(loader)
|
328 | }
|
329 |
|
330 | if (type === 'template') {
|
331 | loader = ensureBang(loader) + ensureBang(templateCompiler)
|
332 | }
|
333 |
|
334 | return ensureBang(loader)
|
335 | } else {
|
336 |
|
337 | switch (type) {
|
338 | case 'template':
|
339 |
|
340 | const preprocessorOption = { engine: lang, templateOption: options.templateOption || {} }
|
341 | const templatePreprocessor = templatePreprocessorPath + '?' + JSON.stringify(preprocessorOption)
|
342 | return ensureBang(defaultLoaders.html) + ensureBang(templateCompiler) + ensureBang(templatePreprocessor)
|
343 | case 'styles':
|
344 | loader = addCssModulesToLoader(defaultLoaders.css, part, index)
|
345 | loader = replaceCssLoader(loader)
|
346 | return ensureBang(loader) + ensureBang(styleCompiler) + ensureBang(ensureLoader(lang))
|
347 | case 'script':
|
348 | return ensureBang(defaultLoaders.js) + ensureBang(ensureLoader(lang))
|
349 | default:
|
350 | loader = loaders[type]
|
351 | if (Array.isArray(loader)) {
|
352 | loader = stringifyLoaders(loader)
|
353 | }
|
354 | return ensureBang(loader + buildCustomBlockLoaderString(part.attrs))
|
355 | }
|
356 | }
|
357 | }
|
358 |
|
359 | function getSelectorString (type, index) {
|
360 | return ensureBang(
|
361 | selectorPath +
|
362 | '?type=' +
|
363 | (type === 'script' || type === 'template' || type === 'styles' || type === 'json'
|
364 | ? type
|
365 | : 'customBlocks') +
|
366 | '&index=' + index
|
367 | )
|
368 | }
|
369 |
|
370 | function getExtractorString (type, index, withIssuer) {
|
371 | return ensureBang(
|
372 | extractorPath +
|
373 | '?type=' +
|
374 | (type === 'script' || type === 'template' || type === 'styles' || type === 'json'
|
375 | ? type
|
376 | : 'customBlocks') +
|
377 | '&index=' + index +
|
378 | (withIssuer ? '&issuerResource=' + loaderContext.resource : '')
|
379 | )
|
380 | }
|
381 |
|
382 | return {
|
383 | loaders,
|
384 | getRequire,
|
385 | getImport,
|
386 | getNamedExports,
|
387 | getRequireForSrc,
|
388 | getImportForSrc,
|
389 | getNamedExportsForSrc,
|
390 | getRequestString,
|
391 | getSrcRequestString
|
392 | }
|
393 | }
|