1 | const hash = require('hash-sum')
|
2 | const parseComponent = require('./parser')
|
3 | const createHelpers = require('./helpers')
|
4 | const loaderUtils = require('loader-utils')
|
5 | const InjectDependency = require('./dependency/InjectDependency')
|
6 | const parseRequest = require('./utils/parse-request')
|
7 | const matchCondition = require('./utils/match-condition')
|
8 | const fixUsingComponent = require('./utils/fix-using-component')
|
9 | const addQuery = require('./utils/add-query')
|
10 | const async = require('async')
|
11 | const processJSON = require('./web/processJSON')
|
12 | const processScript = require('./web/processScript')
|
13 | const processStyles = require('./web/processStyles')
|
14 | const processTemplate = require('./web/processTemplate')
|
15 | const readJsonForSrc = require('./utils/read-json-for-src')
|
16 | const normalize = require('./utils/normalize')
|
17 |
|
18 | module.exports = function (content) {
|
19 | this.cacheable()
|
20 |
|
21 | const mpx = this._compilation.__mpx__
|
22 | if (!mpx) {
|
23 | return content
|
24 | }
|
25 | const packageName = mpx.currentPackageRoot || 'main'
|
26 | const pagesMap = mpx.pagesMap
|
27 | const componentsMap = mpx.componentsMap[packageName]
|
28 | const resolveMode = mpx.resolveMode
|
29 | const projectRoot = mpx.projectRoot
|
30 | const mode = mpx.mode
|
31 | const defs = mpx.defs
|
32 | const i18n = mpx.i18n
|
33 | const globalSrcMode = mpx.srcMode
|
34 | const localSrcMode = loaderUtils.parseQuery(this.resourceQuery || '?').mode
|
35 | const resourcePath = parseRequest(this.resource).resourcePath
|
36 | const srcMode = localSrcMode || globalSrcMode
|
37 | const vueContentCache = mpx.vueContentCache
|
38 | const autoScope = matchCondition(resourcePath, mpx.autoScopeRules)
|
39 |
|
40 | const resourceQueryObj = loaderUtils.parseQuery(this.resourceQuery || '?')
|
41 |
|
42 |
|
43 | if ((resourceQueryObj.component && !componentsMap[resourcePath]) || (resourceQueryObj.page && !pagesMap[resourcePath])) {
|
44 | let entryChunkName
|
45 | const rawRequest = this._module.rawRequest
|
46 | const _preparedEntrypoints = this._compilation._preparedEntrypoints
|
47 | for (let i = 0; i < _preparedEntrypoints.length; i++) {
|
48 | if (rawRequest === _preparedEntrypoints[i].request) {
|
49 | entryChunkName = _preparedEntrypoints[i].name
|
50 | break
|
51 | }
|
52 | }
|
53 | if (resourceQueryObj.component) {
|
54 | componentsMap[resourcePath] = entryChunkName || 'noEntryComponent'
|
55 | } else {
|
56 | pagesMap[resourcePath] = entryChunkName || 'noEntryPage'
|
57 | }
|
58 | }
|
59 |
|
60 | let ctorType = 'app'
|
61 | if (pagesMap[resourcePath]) {
|
62 |
|
63 | ctorType = 'page'
|
64 | } else if (componentsMap[resourcePath]) {
|
65 |
|
66 | ctorType = 'component'
|
67 | }
|
68 |
|
69 | const loaderContext = this
|
70 | const stringifyRequest = r => loaderUtils.stringifyRequest(loaderContext, r)
|
71 | const isProduction = this.minimize || process.env.NODE_ENV === 'production'
|
72 | const options = loaderUtils.getOptions(this) || {}
|
73 |
|
74 | const filePath = this.resourcePath
|
75 |
|
76 | const moduleId = 'm' + hash(this._module.identifier())
|
77 |
|
78 | const needCssSourceMap = (
|
79 | !isProduction &&
|
80 | this.sourceMap &&
|
81 | options.cssSourceMap !== false
|
82 | )
|
83 |
|
84 | const parts = parseComponent(content, filePath, this.sourceMap, mode, defs)
|
85 |
|
86 | let output = ''
|
87 | const callback = this.async()
|
88 |
|
89 | async.waterfall([
|
90 | (callback) => {
|
91 | const json = parts.json || {}
|
92 | if (json.src) {
|
93 | readJsonForSrc(json.src, loaderContext, (err, result) => {
|
94 | if (err) return callback(err)
|
95 | json.content = result
|
96 | callback()
|
97 | })
|
98 | } else {
|
99 | callback()
|
100 | }
|
101 | },
|
102 | (callback) => {
|
103 |
|
104 | if (vueContentCache.has(filePath)) {
|
105 | return callback(null, vueContentCache.get(filePath))
|
106 | }
|
107 |
|
108 | const hasScoped = (parts.styles.some(({ scoped }) => scoped) || autoScope) && mode === 'ali'
|
109 | const templateAttrs = parts.template && parts.template.attrs
|
110 | const hasComment = templateAttrs && templateAttrs.comments
|
111 | const isNative = false
|
112 |
|
113 | let usingComponents = [].concat(Object.keys(mpx.usingComponents))
|
114 |
|
115 | if (parts.json && parts.json.content) {
|
116 | try {
|
117 | let ret = JSON.parse(parts.json.content)
|
118 | if (ret.usingComponents) {
|
119 | fixUsingComponent({ usingComponents: ret.usingComponents, mode })
|
120 | usingComponents = usingComponents.concat(Object.keys(ret.usingComponents))
|
121 | }
|
122 | } catch (e) {
|
123 | return callback(e)
|
124 | }
|
125 | }
|
126 |
|
127 | const {
|
128 | getRequire,
|
129 | getNamedExports,
|
130 | getRequireForSrc,
|
131 | getNamedExportsForSrc
|
132 | } = createHelpers(
|
133 | loaderContext,
|
134 | options,
|
135 | moduleId,
|
136 | isProduction,
|
137 | hasScoped,
|
138 | hasComment,
|
139 | usingComponents,
|
140 | needCssSourceMap,
|
141 | srcMode,
|
142 | isNative,
|
143 | projectRoot
|
144 | )
|
145 |
|
146 |
|
147 | if (mode === 'web') {
|
148 | if (ctorType === 'app' && !resourceQueryObj.app) {
|
149 | const request = addQuery(this.resource, { app: true })
|
150 | output += `
|
151 | import App from ${stringifyRequest(request)}
|
152 | import Vue from 'vue'
|
153 | new Vue({
|
154 | el: '#app',
|
155 | render: function(h){
|
156 | return h(App)
|
157 | }
|
158 | })\n
|
159 | `
|
160 |
|
161 | this.loaderIndex = -1
|
162 | return callback(null, output)
|
163 | }
|
164 |
|
165 | return async.waterfall([
|
166 | (callback) => {
|
167 | async.parallel([
|
168 | (callback) => {
|
169 | processTemplate(parts.template, {
|
170 | mode,
|
171 | srcMode,
|
172 | defs,
|
173 | loaderContext,
|
174 | ctorType
|
175 | }, callback)
|
176 | },
|
177 | (callback) => {
|
178 | processStyles(parts.styles, {
|
179 | ctorType
|
180 | }, callback)
|
181 | },
|
182 | (callback) => {
|
183 | processJSON(parts.json, {
|
184 | mode,
|
185 | defs,
|
186 | resolveMode,
|
187 | loaderContext,
|
188 | pagesMap,
|
189 | pagesEntryMap: mpx.pagesEntryMap,
|
190 | componentsMap,
|
191 | projectRoot
|
192 | }, callback)
|
193 | }
|
194 | ], (err, res) => {
|
195 | callback(err, res)
|
196 | })
|
197 | },
|
198 | ([templateRes, stylesRes, jsonRes], callback) => {
|
199 | output += templateRes.output
|
200 | output += stylesRes.output
|
201 | output += jsonRes.output
|
202 | if (ctorType === 'app' && jsonRes.jsonObj.window && jsonRes.jsonObj.window.navigationBarTitleText) {
|
203 | mpx.appTitle = jsonRes.jsonObj.window.navigationBarTitleText
|
204 | }
|
205 |
|
206 | let pageTitle = ''
|
207 | if (ctorType === 'page' && jsonRes.jsonObj.navigationBarTitleText) {
|
208 | pageTitle = jsonRes.jsonObj.navigationBarTitleText
|
209 | }
|
210 |
|
211 | processScript(parts.script, {
|
212 | ctorType,
|
213 | srcMode,
|
214 | loaderContext,
|
215 | isProduction,
|
216 | getRequireForSrc,
|
217 | i18n,
|
218 | pageTitle,
|
219 | mpxCid: resourceQueryObj.mpxCid,
|
220 | builtInComponentsMap: templateRes.builtInComponentsMap,
|
221 | localComponentsMap: jsonRes.localComponentsMap,
|
222 | localPagesMap: jsonRes.localPagesMap
|
223 | }, callback)
|
224 | }
|
225 | ], (err, scriptRes) => {
|
226 | if (err) return callback(err)
|
227 | output += scriptRes.output
|
228 | vueContentCache.set(filePath, output)
|
229 | callback(null, output)
|
230 | })
|
231 | }
|
232 |
|
233 |
|
234 | output += 'global.currentModuleId\n'
|
235 |
|
236 |
|
237 |
|
238 | let globalInjectCode = `global.currentModuleId = ${JSON.stringify(moduleId)}\n`
|
239 | if (!isProduction) {
|
240 | globalInjectCode += `global.currentResource = ${JSON.stringify(filePath)}\n`
|
241 | }
|
242 | if (ctorType === 'app' && i18n) {
|
243 | globalInjectCode += `global.i18n = ${JSON.stringify({ locale: i18n.locale })}\n`
|
244 |
|
245 | const i18nMethodsVar = 'i18nMethods'
|
246 | const i18nWxsPath = normalize.lib('runtime/i18n.wxs')
|
247 | const i18nWxsLoaderPath = normalize.lib('wxs/wxs-i18n-loader.js')
|
248 | const i18nWxsRequest = i18nWxsLoaderPath + '!' + i18nWxsPath
|
249 | const expression = `require(${loaderUtils.stringifyRequest(loaderContext, i18nWxsRequest)})`
|
250 | const deps = []
|
251 | this._module.parser.parse(expression, {
|
252 | current: {
|
253 | addDependency: dep => {
|
254 | dep.userRequest = i18nMethodsVar
|
255 | deps.push(dep)
|
256 | }
|
257 | },
|
258 | module: this._module
|
259 | })
|
260 | this._module.addVariable(i18nMethodsVar, expression, deps)
|
261 |
|
262 | globalInjectCode += `global.i18nMethods = ${i18nMethodsVar}\n`
|
263 | }
|
264 |
|
265 | let ctor = 'App'
|
266 | if (ctorType === 'page') {
|
267 | if (mpx.forceUsePageCtor || mode === 'ali') {
|
268 | ctor = 'Page'
|
269 | } else {
|
270 | ctor = 'Component'
|
271 | }
|
272 | } else if (ctorType === 'component') {
|
273 | ctor = 'Component'
|
274 | }
|
275 | globalInjectCode += `global.currentCtor = ${ctor}\n`
|
276 | globalInjectCode += `global.currentCtorType = ${JSON.stringify(ctor.replace(/^./, (match) => {
|
277 | return match.toLowerCase()
|
278 | }))}\n`
|
279 |
|
280 |
|
281 |
|
282 | output += '/* script */\n'
|
283 | let scriptSrcMode = srcMode
|
284 | const script = parts.script
|
285 | if (script) {
|
286 | scriptSrcMode = script.mode || scriptSrcMode
|
287 | if (script.src) {
|
288 |
|
289 | script.src = addQuery(script.src, { resourcePath })
|
290 | output += getNamedExportsForSrc('script', script) + '\n\n'
|
291 | } else {
|
292 | output += getNamedExports('script', script) + '\n\n'
|
293 | }
|
294 | } else {
|
295 | switch (ctorType) {
|
296 | case 'app':
|
297 | output += 'import {createApp} from "@mpxjs/core"\n' +
|
298 | 'createApp({})\n'
|
299 | break
|
300 | case 'page':
|
301 | output += 'import {createPage} from "@mpxjs/core"\n' +
|
302 | 'createPage({})\n'
|
303 | break
|
304 | case 'component':
|
305 | output += 'import {createComponent} from "@mpxjs/core"\n' +
|
306 | 'createComponent({})\n'
|
307 | }
|
308 | output += '\n'
|
309 | }
|
310 |
|
311 | if (scriptSrcMode) {
|
312 | globalInjectCode += `global.currentSrcMode = ${JSON.stringify(scriptSrcMode)}\n`
|
313 | }
|
314 |
|
315 |
|
316 | output += '/* styles */\n'
|
317 | let cssModules
|
318 | if (parts.styles.length) {
|
319 | let styleInjectionCode = ''
|
320 | parts.styles.forEach((style, i) => {
|
321 | let scoped = hasScoped ? (style.scoped || autoScope) : false
|
322 |
|
323 |
|
324 | let requireString = style.src
|
325 | ? getRequireForSrc('styles', style, -1, scoped, undefined, true)
|
326 | : getRequire('styles', style, i, scoped)
|
327 |
|
328 | const hasStyleLoader = requireString.indexOf('style-loader') > -1
|
329 | const invokeStyle = code => `${code}\n`
|
330 |
|
331 | const moduleName = style.module === true ? '$style' : style.module
|
332 |
|
333 | if (moduleName) {
|
334 | if (!cssModules) {
|
335 | cssModules = {}
|
336 | }
|
337 | if (moduleName in cssModules) {
|
338 | loaderContext.emitError(
|
339 | 'CSS module name "' + moduleName + '" is not unique!'
|
340 | )
|
341 | styleInjectionCode += invokeStyle(requireString)
|
342 | } else {
|
343 | cssModules[moduleName] = true
|
344 |
|
345 | if (!hasStyleLoader) {
|
346 | requireString += '.locals'
|
347 | }
|
348 |
|
349 | styleInjectionCode += invokeStyle(
|
350 | 'this["' + moduleName + '"] = ' + requireString
|
351 | )
|
352 | }
|
353 | } else {
|
354 | styleInjectionCode += invokeStyle(requireString)
|
355 | }
|
356 | })
|
357 | output += styleInjectionCode + '\n'
|
358 | }
|
359 |
|
360 |
|
361 | output += '/* json */\n'
|
362 |
|
363 | const json = parts.json || {}
|
364 | if (json.src) {
|
365 | json.src = addQuery(json.src, { resourcePath, __component: true })
|
366 | output += getRequireForSrc('json', json) + '\n\n'
|
367 | } else {
|
368 | output += getRequire('json', json) + '\n\n'
|
369 | }
|
370 |
|
371 |
|
372 | output += '/* template */\n'
|
373 | const template = parts.template
|
374 |
|
375 | if (template) {
|
376 | if (template.src) {
|
377 | template.src = addQuery(template.src, { resourcePath })
|
378 | output += getRequireForSrc('template', template) + '\n\n'
|
379 | } else {
|
380 | output += getRequire('template', template) + '\n\n'
|
381 | }
|
382 | }
|
383 |
|
384 | if (!mpx.forceDisableInject) {
|
385 | const dep = new InjectDependency({
|
386 | content: globalInjectCode,
|
387 | index: -3
|
388 | })
|
389 | this._module.addDependency(dep)
|
390 | }
|
391 |
|
392 | callback(null, output)
|
393 | }
|
394 | ], callback)
|
395 | }
|