glass-easel-miniprogram-webpack-plugin 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/helpers.js +7 -0
- package/index.js +362 -0
- package/package.json +33 -0
- package/wxss_loader.js +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# glass-easel-miniprogram-webpack-plugin
|
|
2
|
+
|
|
3
|
+
The webpack plugin for building mini-program code running in glass-easel.
|
|
4
|
+
|
|
5
|
+
Refer to the [glass-easel](https://github.com/wechat-miniprogram/glass-easel) project for further details.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
See the [template](../glass-easel-miniprogram-template/).
|
package/helpers.js
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
const fs = require('fs').promises
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const { NormalModule } = require('webpack')
|
|
6
|
+
const { RawSource } = require('webpack-sources')
|
|
7
|
+
const VirtualModulesPlugin = require('webpack-virtual-modules')
|
|
8
|
+
const chokidar = require('chokidar')
|
|
9
|
+
const { TmplGroup } = require('glass-easel-template-compiler')
|
|
10
|
+
|
|
11
|
+
const { escapeJsString } = require('./helpers')
|
|
12
|
+
|
|
13
|
+
const GlassEaselMiniprogramWxssLoader = path.join(__dirname, 'wxss_loader.js')
|
|
14
|
+
|
|
15
|
+
const PLUGIN_NAME = 'GlassEaselMiniprogramWebpackPlugin'
|
|
16
|
+
|
|
17
|
+
class StyleSheetManager {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.map = Object.create(null)
|
|
20
|
+
this.enableStyleScope = Object.create(null)
|
|
21
|
+
this.scopeNameInc = 0
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
add(compPath, srcPath) {
|
|
25
|
+
let scopeNameNum = this.scopeNameInc
|
|
26
|
+
this.scopeNameInc += 1
|
|
27
|
+
let scopeName = ''
|
|
28
|
+
do {
|
|
29
|
+
const n = scopeNameNum % 52
|
|
30
|
+
let c
|
|
31
|
+
if (n >= 26) {
|
|
32
|
+
c = String.fromCharCode(n - 26 + 97)
|
|
33
|
+
} else {
|
|
34
|
+
c = String.fromCharCode(n + 65)
|
|
35
|
+
}
|
|
36
|
+
scopeName += c
|
|
37
|
+
scopeNameNum = Math.floor(scopeNameNum / 52)
|
|
38
|
+
} while (scopeNameNum > 0)
|
|
39
|
+
this.map[compPath] = {
|
|
40
|
+
srcPath,
|
|
41
|
+
scopeName,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
setStyleIsolation(compPath, styleIsolation, isComponent) {
|
|
46
|
+
const enabled = styleIsolation
|
|
47
|
+
? styleIsolation !== 'shared' && styleIsolation !== 'page-shared'
|
|
48
|
+
: isComponent
|
|
49
|
+
this.enableStyleScope[compPath] = enabled
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getScopeName(compPath) {
|
|
53
|
+
if (this.enableStyleScope[compPath]) {
|
|
54
|
+
return this.map[compPath].scopeName
|
|
55
|
+
}
|
|
56
|
+
return undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
toCodeString() {
|
|
60
|
+
const arr = Object.entries(this.map).map(([compPath, { srcPath }]) => {
|
|
61
|
+
const s = `backend.registerStyleSheetContent('${escapeJsString(compPath)}', require('${escapeJsString(srcPath)}'));`
|
|
62
|
+
return s
|
|
63
|
+
})
|
|
64
|
+
return `
|
|
65
|
+
function (backend) { ${arr.join('')} }
|
|
66
|
+
`
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
class GlassEaselMiniprogramWebpackPlugin {
|
|
71
|
+
constructor(options) {
|
|
72
|
+
this.path = options.path || './src'
|
|
73
|
+
this.resourceFilePattern = options.resourceFilePattern || /\.(jpg|jpeg|png|gif|html)$/
|
|
74
|
+
this.defaultEntry = 'pages/index/index'
|
|
75
|
+
this.virtualModules = new VirtualModulesPlugin()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
apply(compiler) {
|
|
79
|
+
// search paths
|
|
80
|
+
const codeRoot = path.resolve(this.path)
|
|
81
|
+
const params = {
|
|
82
|
+
globalStaticConfig: {},
|
|
83
|
+
compInfoMap: Object.create(null),
|
|
84
|
+
resPathMap: Object.create(null),
|
|
85
|
+
appEntry: null,
|
|
86
|
+
tmplGroup: new TmplGroup(),
|
|
87
|
+
styleSheetManager: new StyleSheetManager(),
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// determine a path is a component path or not, returning the json content if true
|
|
91
|
+
const isCompPath = async (relPath) => {
|
|
92
|
+
if (!relPath) return null
|
|
93
|
+
let staticConfig = null
|
|
94
|
+
try {
|
|
95
|
+
const json = await fs.readFile(path.join(codeRoot, `${relPath}.json`), { encoding: 'utf8' })
|
|
96
|
+
const parsed = JSON.parse(json)
|
|
97
|
+
if (parsed && (parsed.component === true || typeof parsed.usingComponents === 'object')) {
|
|
98
|
+
staticConfig = parsed
|
|
99
|
+
}
|
|
100
|
+
} catch (e) { /* empty */ }
|
|
101
|
+
return staticConfig
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// determine a file is located in the code root or not, returning relative path if true
|
|
105
|
+
const normalizePath = (absPath) => {
|
|
106
|
+
const p = path.relative(codeRoot, absPath)
|
|
107
|
+
if (p.split(path.sep, 1)[0] === '..') return null
|
|
108
|
+
return p.split(path.sep).join('/')
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// search for component files
|
|
112
|
+
let codeRootWatching = false
|
|
113
|
+
const searchCodeRoot = async (enableWatch) => {
|
|
114
|
+
if (codeRootWatching) return
|
|
115
|
+
codeRootWatching = true
|
|
116
|
+
const handleFile = async (relPath) => {
|
|
117
|
+
// for app.json, spread the global field
|
|
118
|
+
if (relPath === 'app.json') {
|
|
119
|
+
try {
|
|
120
|
+
const json = await fs.readFile(path.join(codeRoot, relPath), { encoding: 'utf8' })
|
|
121
|
+
const staticConfig = JSON.parse(json)
|
|
122
|
+
if (staticConfig.usingComponents) {
|
|
123
|
+
params.globalStaticConfig = {
|
|
124
|
+
usingComponents: staticConfig.usingComponents,
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} catch (e) {
|
|
128
|
+
params.globalStaticConfig = {}
|
|
129
|
+
}
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// for app.ts or app.js, load it first
|
|
134
|
+
if (relPath === 'app.ts' || relPath === 'app.js') {
|
|
135
|
+
params.appEntry = relPath
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// find component by json files
|
|
139
|
+
const extName = path.extname(relPath)
|
|
140
|
+
if (extName === '.json') {
|
|
141
|
+
const staticConfig = await isCompPath(relPath.slice(0, -extName.length))
|
|
142
|
+
if (staticConfig) {
|
|
143
|
+
const compPath = relPath.slice(0, -5)
|
|
144
|
+
try {
|
|
145
|
+
const tsFileStat = await fs.stat(path.join(codeRoot, `${compPath}.ts`))
|
|
146
|
+
if (tsFileStat.isFile()) {
|
|
147
|
+
params.compInfoMap[compPath] = {
|
|
148
|
+
main: `${compPath}.ts`,
|
|
149
|
+
staticConfig,
|
|
150
|
+
}
|
|
151
|
+
params.styleSheetManager.setStyleIsolation(
|
|
152
|
+
compPath,
|
|
153
|
+
staticConfig.styleIsolation,
|
|
154
|
+
!!staticConfig.component,
|
|
155
|
+
)
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
} catch (e) { /* empty */ }
|
|
159
|
+
try {
|
|
160
|
+
const jsFileStat = await fs.stat(path.join(codeRoot, `${compPath}.js`))
|
|
161
|
+
if (jsFileStat.isFile()) {
|
|
162
|
+
params.compInfoMap[compPath] = {
|
|
163
|
+
main: `${compPath}.js`,
|
|
164
|
+
staticConfig,
|
|
165
|
+
}
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
} catch (e) { /* empty */ }
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// add wxml
|
|
173
|
+
if (extName === '.wxml') {
|
|
174
|
+
const src = await fs.readFile(path.join(codeRoot, relPath), { encoding: 'utf8' })
|
|
175
|
+
params.tmplGroup.addTmpl(relPath.slice(0, -extName.length), src)
|
|
176
|
+
// TODO support wxml file remove
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// add wxss
|
|
180
|
+
if (extName === '.wxss') {
|
|
181
|
+
const srcPath = path.join(codeRoot, relPath)
|
|
182
|
+
params.styleSheetManager.add(relPath.slice(0, -extName.length), srcPath)
|
|
183
|
+
// TODO support wxss file remove
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// find resource files
|
|
187
|
+
if (this.resourceFilePattern.test(relPath)) {
|
|
188
|
+
params.resPathMap[relPath] = true
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const removeEntry = (relPath) => {
|
|
193
|
+
delete params.resPathMap[relPath]
|
|
194
|
+
delete params.compInfoMap[relPath]
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// await readdirp(codeRoot, handleFile)
|
|
198
|
+
await new Promise((resolve, reject) => {
|
|
199
|
+
const promises = []
|
|
200
|
+
const watcher = chokidar.watch(codeRoot, { ignoreInitial: false })
|
|
201
|
+
watcher
|
|
202
|
+
.on('add', (p) => {
|
|
203
|
+
promises.push(handleFile(normalizePath(p)))
|
|
204
|
+
})
|
|
205
|
+
.on('change', (p) => {
|
|
206
|
+
promises.push(handleFile(normalizePath(p)))
|
|
207
|
+
})
|
|
208
|
+
.on('unlink', (p) => {
|
|
209
|
+
removeEntry(normalizePath(p))
|
|
210
|
+
})
|
|
211
|
+
.on('error', (err) => {
|
|
212
|
+
throw new Error(err)
|
|
213
|
+
})
|
|
214
|
+
.on('ready', () => {
|
|
215
|
+
Promise.all(promises)
|
|
216
|
+
.then(() => {
|
|
217
|
+
if (!enableWatch) return watcher.close()
|
|
218
|
+
return null
|
|
219
|
+
})
|
|
220
|
+
.then(resolve)
|
|
221
|
+
.catch(reject)
|
|
222
|
+
})
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// init component list before run
|
|
227
|
+
compiler.hooks.beforeRun.tapPromise(PLUGIN_NAME, async () => {
|
|
228
|
+
await searchCodeRoot(false)
|
|
229
|
+
})
|
|
230
|
+
compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, async () => {
|
|
231
|
+
await searchCodeRoot(true)
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
// rewrite component entry paths
|
|
235
|
+
compiler.resolverFactory.hooks.resolver
|
|
236
|
+
.for('normal')
|
|
237
|
+
.tap(PLUGIN_NAME, (resolver) => {
|
|
238
|
+
resolver.hooks.result.tap(PLUGIN_NAME, (data) => {
|
|
239
|
+
const absPath = data.path
|
|
240
|
+
const extName = path.extname(absPath)
|
|
241
|
+
if (extName === '.js' || extName === '.ts') {
|
|
242
|
+
const relPath = normalizePath(absPath)
|
|
243
|
+
if (relPath && params.compInfoMap[relPath.slice(0, -3)]) {
|
|
244
|
+
const redirected = `${absPath.slice(0, -3)}.component`
|
|
245
|
+
if (data.context.issuer !== redirected) {
|
|
246
|
+
data.path = redirected
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return data
|
|
251
|
+
})
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
// add loaders
|
|
255
|
+
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
256
|
+
NormalModule.getCompilationHooks(compilation).beforeLoaders
|
|
257
|
+
.tap(PLUGIN_NAME, (loaders, mod) => {
|
|
258
|
+
const absPath = mod.resource
|
|
259
|
+
const extName = path.extname(absPath)
|
|
260
|
+
if (extName === '.ts' || extName === '.js' || extName === '.wxml' || extName === '.wxss') {
|
|
261
|
+
const relPath = path.relative(codeRoot, absPath).split(path.sep).join('/')
|
|
262
|
+
const compPath = relPath.slice(0, -extName.length)
|
|
263
|
+
if (params.compInfoMap[compPath] || compPath === 'app') {
|
|
264
|
+
if (extName === '.wxss') {
|
|
265
|
+
loaders.forEach((x) => {
|
|
266
|
+
if (x.loader === GlassEaselMiniprogramWxssLoader) {
|
|
267
|
+
x.options = {
|
|
268
|
+
classPrefix: params.styleSheetManager.getScopeName(compPath),
|
|
269
|
+
relPath,
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
// collect virtual files
|
|
280
|
+
const virtualModules = this.virtualModules
|
|
281
|
+
virtualModules.apply(compiler)
|
|
282
|
+
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
283
|
+
// add virtual component js file
|
|
284
|
+
Object.keys(params.compInfoMap).forEach((compPath) => {
|
|
285
|
+
const compInfo = params.compInfoMap[compPath]
|
|
286
|
+
const json = JSON.stringify(compInfo.staticConfig)
|
|
287
|
+
const scopeName = params.styleSheetManager.getScopeName(compPath)
|
|
288
|
+
const scopeNameStr = scopeName === undefined ? undefined : `'${scopeName}'`
|
|
289
|
+
virtualModules.writeModule(
|
|
290
|
+
path.join(codeRoot, `${compPath}.component`),
|
|
291
|
+
`
|
|
292
|
+
var index = require('${escapeJsString(codeRoot)}/index.js')
|
|
293
|
+
index.codeSpace.addComponentStaticConfig('${escapeJsString(compPath)}', ${json})
|
|
294
|
+
index.codeSpace.addCompiledTemplate('${escapeJsString(compPath)}', {
|
|
295
|
+
groupList: index.genObjectGroups,
|
|
296
|
+
content: index.genObjectGroups['${escapeJsString(compPath)}']
|
|
297
|
+
})
|
|
298
|
+
index.codeSpace.addStyleSheet(
|
|
299
|
+
'${escapeJsString(compPath)}',
|
|
300
|
+
'${escapeJsString(compPath)}',
|
|
301
|
+
${scopeNameStr},
|
|
302
|
+
)
|
|
303
|
+
index.codeSpace.globalComponentEnv(index.globalObject, '${escapeJsString(compPath)}', () => {
|
|
304
|
+
require('./${escapeJsString(path.basename(compInfo.main))}')
|
|
305
|
+
})
|
|
306
|
+
`,
|
|
307
|
+
)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
// add virtual index file
|
|
311
|
+
const entryHeader = `
|
|
312
|
+
var adapter = require('glass-easel-miniprogram-adapter')
|
|
313
|
+
var glassEasel = adapter.glassEasel
|
|
314
|
+
var env = new adapter.MiniProgramEnv()
|
|
315
|
+
exports.env = env
|
|
316
|
+
var backend = new glassEasel.domlikeBackend.CurrentWindowBackendContext()
|
|
317
|
+
backend.onEvent((target, type, detail, options) => {
|
|
318
|
+
let cur = target
|
|
319
|
+
while (cur && !cur.__wxElement) cur = cur.parentNode
|
|
320
|
+
if (!cur) return
|
|
321
|
+
glassEasel.triggerEvent(target.__wxElement, type, detail, options)
|
|
322
|
+
})
|
|
323
|
+
var ab = env.associateBackend(backend)
|
|
324
|
+
;(${params.styleSheetManager.toCodeString()})(ab)
|
|
325
|
+
var codeSpace = env.createCodeSpace('', true)
|
|
326
|
+
codeSpace.addStyleSheet('app', 'app')
|
|
327
|
+
exports.codeSpace = codeSpace
|
|
328
|
+
exports.genObjectGroups = ${params.tmplGroup.getTmplGenObjectGroups()}
|
|
329
|
+
exports.globalObject = (function () {
|
|
330
|
+
if (typeof this !== 'undefined') { return this }
|
|
331
|
+
if (typeof globalThis !== 'undefined') { return globalThis }
|
|
332
|
+
if (typeof self !== 'undefined') { return self }
|
|
333
|
+
if (typeof window !== 'undefined') { return window }
|
|
334
|
+
if (typeof global !== 'undefined') { return global }
|
|
335
|
+
throw new Error('The global object cannot be recognized')
|
|
336
|
+
})()
|
|
337
|
+
`
|
|
338
|
+
const entryFooter = `
|
|
339
|
+
var root = ab.createRoot('glass-easel-root', codeSpace, '${escapeJsString(this.defaultEntry)}')
|
|
340
|
+
var placeholder = document.createElement('span')
|
|
341
|
+
document.body.appendChild(placeholder)
|
|
342
|
+
root.attach(document.body, placeholder)
|
|
343
|
+
`
|
|
344
|
+
const entries = Object.values(params.compInfoMap).map((compInfo) => compInfo.main)
|
|
345
|
+
if (params.appEntry) entries.unshift(params.appEntry)
|
|
346
|
+
virtualModules.writeModule(
|
|
347
|
+
`${codeRoot}/index.js`,
|
|
348
|
+
entryHeader + entries.map((p) => `require('./${escapeJsString(p)}')\n`).join('') + entryFooter,
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
// copy res files
|
|
352
|
+
compilation.hooks.additionalAssets.tapPromise(PLUGIN_NAME, async () => {
|
|
353
|
+
await Promise.all(Object.keys(params.resPathMap).map(async (p) => {
|
|
354
|
+
compilation.assets[p] = new RawSource(await fs.readFile(path.join(codeRoot, p)))
|
|
355
|
+
}))
|
|
356
|
+
})
|
|
357
|
+
})
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
exports.GlassEaselMiniprogramWebpackPlugin = GlassEaselMiniprogramWebpackPlugin
|
|
362
|
+
exports.GlassEaselMiniprogramWxssLoader = GlassEaselMiniprogramWxssLoader
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "glass-easel-miniprogram-webpack-plugin",
|
|
3
|
+
"description": "The webpack plugin of the glass-easel project for MiniProgram file structure",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/wechat-miniprogram/glass-easel.git"
|
|
8
|
+
},
|
|
9
|
+
"keywords": ["glass-easel"],
|
|
10
|
+
"author": "wechat-miniprogram",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/wechat-miniprogram/glass-easel/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/wechat-miniprogram/glass-easel",
|
|
16
|
+
"main": "index.js",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"lint": "eslint src/**/*.ts"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"glass-easel": "0.1",
|
|
22
|
+
"glass-easel-miniprogram-adapter": "0.1",
|
|
23
|
+
"webpack": "^5.52.1"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"chokidar": "^3.5.3",
|
|
27
|
+
"glass-easel-stylesheet-compiler": "0.1",
|
|
28
|
+
"glass-easel-template-compiler": "0.1",
|
|
29
|
+
"source-map": "^0.7.4",
|
|
30
|
+
"webpack-sources": "^3.2.1",
|
|
31
|
+
"webpack-virtual-modules": "^0.4.3"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/wxss_loader.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
const { SourceMapGenerator, SourceMapConsumer } = require('source-map')
|
|
4
|
+
const { StyleSheetTransformer } = require('glass-easel-stylesheet-compiler')
|
|
5
|
+
|
|
6
|
+
module.exports = function (src, prevMap, meta) {
|
|
7
|
+
const callback = this.async()
|
|
8
|
+
const { classPrefix } = this.query
|
|
9
|
+
const sst = new StyleSheetTransformer(this.resourcePath, src, classPrefix, 750)
|
|
10
|
+
const ss = sst.getContent()
|
|
11
|
+
let map
|
|
12
|
+
if (this.sourceMap) {
|
|
13
|
+
const ssSourceMap = JSON.parse(sst.toSourceMap())
|
|
14
|
+
if (prevMap) {
|
|
15
|
+
const destConsumer = new SourceMapConsumer(ssSourceMap)
|
|
16
|
+
const srcConsumer = new SourceMapConsumer(prevMap)
|
|
17
|
+
Promise.all([destConsumer, srcConsumer]).then(([destConsumer, srcConsumer]) => {
|
|
18
|
+
const gen = SourceMapGenerator.fromSourceMap(destConsumer)
|
|
19
|
+
gen.applySourceMap(srcConsumer, this.resourcePath)
|
|
20
|
+
destConsumer.destroy()
|
|
21
|
+
srcConsumer.destroy()
|
|
22
|
+
map = gen.toJSON()
|
|
23
|
+
callback(null, ss, map, meta)
|
|
24
|
+
return undefined
|
|
25
|
+
}).catch((err) => {
|
|
26
|
+
callback(err)
|
|
27
|
+
})
|
|
28
|
+
} else {
|
|
29
|
+
map = ssSourceMap
|
|
30
|
+
callback(null, ss, map, meta)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|