glass-easel-miniprogram-webpack-plugin 0.2.0 → 0.3.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/.eslintignore +6 -0
- package/README.md +14 -0
- package/{helpers.js → helpers.ts} +1 -1
- package/index.js +506 -389
- package/index.ts +565 -0
- package/package.json +7 -6
- package/tsconfig.json +9 -0
- package/wxml_loader.js +8 -2
package/index.ts
ADDED
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
2
|
+
|
|
3
|
+
import { promises as fs } from 'node:fs'
|
|
4
|
+
import * as path from 'node:path'
|
|
5
|
+
import { NormalModule, type Compiler, type WebpackPluginInstance } from 'webpack'
|
|
6
|
+
import { TmplGroup } from 'glass-easel-template-compiler'
|
|
7
|
+
import { escapeJsString } from './helpers'
|
|
8
|
+
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
10
|
+
const chokidar = require('chokidar')
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
12
|
+
const { RawSource } = require('webpack-sources')
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
14
|
+
const VirtualModulesPlugin = require('webpack-virtual-modules')
|
|
15
|
+
|
|
16
|
+
export const GlassEaselMiniprogramWxmlLoader = path.join(__dirname, 'wxml_loader.js')
|
|
17
|
+
export const GlassEaselMiniprogramWxssLoader = path.join(__dirname, 'wxss_loader.js')
|
|
18
|
+
|
|
19
|
+
const PLUGIN_NAME = 'GlassEaselMiniprogramWebpackPlugin'
|
|
20
|
+
const CACHE_ETAG = 1
|
|
21
|
+
|
|
22
|
+
type PluginConfig = {
|
|
23
|
+
path: string
|
|
24
|
+
resourceFilePattern: RegExp
|
|
25
|
+
defaultEntry: string
|
|
26
|
+
customBootstrap: boolean
|
|
27
|
+
disableClassPrefix: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type GlobalStaticConfig = {
|
|
31
|
+
usingComponents?: { [tagName: string]: string }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type ComponentStaticConfig = {
|
|
35
|
+
component: boolean
|
|
36
|
+
usingComponents: { [tagName: string]: string }
|
|
37
|
+
styleIsolation:
|
|
38
|
+
| 'isolated'
|
|
39
|
+
| 'apply-shared'
|
|
40
|
+
| 'shared'
|
|
41
|
+
| 'page-isolated'
|
|
42
|
+
| 'page-apply-shared'
|
|
43
|
+
| 'page-shared'
|
|
44
|
+
pureDataPattern: string
|
|
45
|
+
taskConfig: unknown
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const readCache = <T>(compiler: Compiler, key: string): Promise<T | undefined> =>
|
|
49
|
+
new Promise((resolve) => {
|
|
50
|
+
const cacheKey = `${PLUGIN_NAME}|${key}`
|
|
51
|
+
compiler.cache.get(cacheKey, CACHE_ETAG, (err, cache) => {
|
|
52
|
+
if (!err && cache !== undefined) {
|
|
53
|
+
resolve(cache as T)
|
|
54
|
+
} else {
|
|
55
|
+
resolve(undefined)
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const writeCache = <T>(compiler: Compiler, key: string, value: T): Promise<void> =>
|
|
61
|
+
new Promise((resolve) => {
|
|
62
|
+
const cacheKey = `${PLUGIN_NAME}|${key}`
|
|
63
|
+
compiler.cache.store(cacheKey, CACHE_ETAG, value, (_err) => {
|
|
64
|
+
resolve()
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
class StyleSheetManager {
|
|
69
|
+
map = Object.create(null) as { [path: string]: { scopeName: string; srcPath: string } }
|
|
70
|
+
enableStyleScope = Object.create(null) as { [path: string]: boolean }
|
|
71
|
+
scopeNameInc = 0
|
|
72
|
+
disableClassPrefix: boolean
|
|
73
|
+
|
|
74
|
+
constructor(disableClassPrefix: boolean) {
|
|
75
|
+
this.disableClassPrefix = disableClassPrefix
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
add(compPath: string, srcPath: string) {
|
|
79
|
+
let scopeNameNum = this.scopeNameInc
|
|
80
|
+
this.scopeNameInc += 1
|
|
81
|
+
let scopeName = ''
|
|
82
|
+
do {
|
|
83
|
+
const n = scopeNameNum % 52
|
|
84
|
+
let c
|
|
85
|
+
if (n >= 26) {
|
|
86
|
+
c = String.fromCharCode(n - 26 + 97)
|
|
87
|
+
} else {
|
|
88
|
+
c = String.fromCharCode(n + 65)
|
|
89
|
+
}
|
|
90
|
+
scopeName += c
|
|
91
|
+
scopeNameNum = Math.floor(scopeNameNum / 52)
|
|
92
|
+
} while (scopeNameNum > 0)
|
|
93
|
+
this.map[compPath] = {
|
|
94
|
+
srcPath,
|
|
95
|
+
scopeName,
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
setStyleIsolation(compPath: string, styleIsolation: string, isComponent: boolean) {
|
|
100
|
+
const enabled = styleIsolation
|
|
101
|
+
? styleIsolation !== 'shared' && styleIsolation !== 'page-shared'
|
|
102
|
+
: isComponent
|
|
103
|
+
this.enableStyleScope[compPath] = enabled
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
getScopeName(compPath: string) {
|
|
107
|
+
if (this.disableClassPrefix) return undefined
|
|
108
|
+
if (this.enableStyleScope[compPath]) {
|
|
109
|
+
return this.map[compPath]?.scopeName
|
|
110
|
+
}
|
|
111
|
+
return undefined
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
toCodeString() {
|
|
115
|
+
const arr = Object.entries(this.map).map(([compPath, { srcPath }]) => {
|
|
116
|
+
const s = `backend.registerStyleSheetContent('${escapeJsString(
|
|
117
|
+
compPath,
|
|
118
|
+
)}', require('${escapeJsString(srcPath)}'));`
|
|
119
|
+
return s
|
|
120
|
+
})
|
|
121
|
+
return `
|
|
122
|
+
function (backend) {
|
|
123
|
+
backend.registerStyleSheetContent('app', require('./app.wxss'))
|
|
124
|
+
${arr.join('')}
|
|
125
|
+
}
|
|
126
|
+
`
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export class GlassEaselMiniprogramWebpackPlugin implements WebpackPluginInstance {
|
|
131
|
+
path: string
|
|
132
|
+
resourceFilePattern: RegExp
|
|
133
|
+
defaultEntry: string
|
|
134
|
+
customBootstrap: boolean
|
|
135
|
+
disableClassPrefix: boolean
|
|
136
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
137
|
+
virtualModules = new VirtualModulesPlugin() as {
|
|
138
|
+
apply: (compiler: Compiler) => void
|
|
139
|
+
writeModule: (p: string, c: string) => void
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
constructor(options: Partial<PluginConfig>) {
|
|
143
|
+
this.path = options.path || './src'
|
|
144
|
+
this.resourceFilePattern = options.resourceFilePattern || /\.(jpg|jpeg|png|gif)$/
|
|
145
|
+
this.defaultEntry = options.defaultEntry || 'pages/index/index'
|
|
146
|
+
this.customBootstrap = options.customBootstrap || false
|
|
147
|
+
this.disableClassPrefix = options.disableClassPrefix || false
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
apply(compiler: Compiler) {
|
|
151
|
+
const codeRoot = path.resolve(this.path)
|
|
152
|
+
const compInfoMap = Object.create(null) as {
|
|
153
|
+
[compPath: string]: { main: string; taskConfig: unknown; hasWxss: boolean }
|
|
154
|
+
}
|
|
155
|
+
const resPathMap = Object.create(null) as { [compPath: string]: true }
|
|
156
|
+
const wxmlContentMap = Object.create(null) as { [compPath: string]: string }
|
|
157
|
+
let globalStaticConfig = {} as GlobalStaticConfig
|
|
158
|
+
let appEntry: 'app.js' | 'app.ts' | null = null
|
|
159
|
+
const depsTmplGroup = new TmplGroup()
|
|
160
|
+
const styleSheetManager = new StyleSheetManager(this.disableClassPrefix)
|
|
161
|
+
|
|
162
|
+
// cleanup wasm modules
|
|
163
|
+
compiler.hooks.shutdown.tap(PLUGIN_NAME, () => {
|
|
164
|
+
depsTmplGroup.free()
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
// determine a file is located in the code root or not, returning relative path if true
|
|
168
|
+
const normalizePath = (absPath: string): string | null => {
|
|
169
|
+
const p = path.relative(codeRoot, absPath)
|
|
170
|
+
if (p.split(path.sep, 1)[0] === '..') return null
|
|
171
|
+
return p.split(path.sep).join('/')
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// determine a path is a component path or not, returning the json content if true
|
|
175
|
+
const isCompPath = async (relPath: string): Promise<ComponentStaticConfig | null> => {
|
|
176
|
+
if (!relPath) return null
|
|
177
|
+
let staticConfig = null
|
|
178
|
+
try {
|
|
179
|
+
const json = await fs.readFile(path.join(codeRoot, `${relPath}.json`), { encoding: 'utf8' })
|
|
180
|
+
const parsed = JSON.parse(json) as ComponentStaticConfig
|
|
181
|
+
if (parsed && (parsed.component === true || typeof parsed.usingComponents === 'object')) {
|
|
182
|
+
staticConfig = parsed
|
|
183
|
+
}
|
|
184
|
+
} catch (e) {
|
|
185
|
+
/* empty */
|
|
186
|
+
}
|
|
187
|
+
return staticConfig
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// search for component files
|
|
191
|
+
let codeRootWatching = false
|
|
192
|
+
const searchCodeRoot = async (enableWatch: boolean) => {
|
|
193
|
+
if (codeRootWatching) return
|
|
194
|
+
codeRootWatching = true
|
|
195
|
+
const handleFile = async (relPath: string) => {
|
|
196
|
+
// for app.json, spread the global field
|
|
197
|
+
if (relPath === 'app.json') {
|
|
198
|
+
try {
|
|
199
|
+
const json = await fs.readFile(path.join(codeRoot, relPath), { encoding: 'utf8' })
|
|
200
|
+
const staticConfig = JSON.parse(json) as GlobalStaticConfig
|
|
201
|
+
if (staticConfig.usingComponents) {
|
|
202
|
+
globalStaticConfig = {
|
|
203
|
+
usingComponents: staticConfig.usingComponents,
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
} catch (e) {
|
|
207
|
+
globalStaticConfig = {}
|
|
208
|
+
}
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// for app.ts or app.js, load it first
|
|
213
|
+
if (relPath === 'app.ts' || relPath === 'app.js') {
|
|
214
|
+
appEntry = relPath
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// find component by json files
|
|
218
|
+
const extName = path.extname(relPath)
|
|
219
|
+
if (extName === '.json') {
|
|
220
|
+
const staticConfig = await isCompPath(relPath.slice(0, -extName.length))
|
|
221
|
+
if (staticConfig) {
|
|
222
|
+
const compPath = relPath.slice(0, -extName.length)
|
|
223
|
+
const absPath = path.join(codeRoot, `${compPath}.wxss`)
|
|
224
|
+
let hasWxss = false
|
|
225
|
+
try {
|
|
226
|
+
hasWxss = (await fs.stat(absPath)).isFile()
|
|
227
|
+
} catch (e) {
|
|
228
|
+
/* empty */
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
const tsFileStat = await fs.stat(path.join(codeRoot, `${compPath}.ts`))
|
|
232
|
+
if (tsFileStat.isFile()) {
|
|
233
|
+
compInfoMap[compPath] = {
|
|
234
|
+
main: `${compPath}.ts`,
|
|
235
|
+
taskConfig: staticConfig.taskConfig,
|
|
236
|
+
hasWxss,
|
|
237
|
+
}
|
|
238
|
+
styleSheetManager.add(compPath, absPath)
|
|
239
|
+
styleSheetManager.setStyleIsolation(
|
|
240
|
+
compPath,
|
|
241
|
+
staticConfig.styleIsolation,
|
|
242
|
+
!!staticConfig.component,
|
|
243
|
+
)
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
} catch (e) {
|
|
247
|
+
/* empty */
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
const jsFileStat = await fs.stat(path.join(codeRoot, `${compPath}.js`))
|
|
251
|
+
if (jsFileStat.isFile()) {
|
|
252
|
+
compInfoMap[compPath] = {
|
|
253
|
+
main: `${compPath}.js`,
|
|
254
|
+
taskConfig: staticConfig.taskConfig,
|
|
255
|
+
hasWxss,
|
|
256
|
+
}
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
} catch (e) {
|
|
260
|
+
/* empty */
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// find wxss file for components
|
|
266
|
+
if (extName === '.wxss') {
|
|
267
|
+
const compPath = relPath.slice(0, -5)
|
|
268
|
+
if (compInfoMap[compPath]) {
|
|
269
|
+
compInfoMap[compPath]!.hasWxss = true
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// find resource files
|
|
274
|
+
if (this.resourceFilePattern.test(relPath)) {
|
|
275
|
+
resPathMap[relPath] = true
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const removeEntry = (relPath: string) => {
|
|
280
|
+
delete resPathMap[relPath]
|
|
281
|
+
const extName = path.extname(relPath)
|
|
282
|
+
const compPath = relPath.slice(0, -extName.length)
|
|
283
|
+
if (compInfoMap[compPath]) {
|
|
284
|
+
if (extName === '.json') {
|
|
285
|
+
delete compInfoMap[compPath]
|
|
286
|
+
} else if (extName === '.wxss') {
|
|
287
|
+
compInfoMap[compPath]!.hasWxss = false
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// await readdirp(codeRoot, handleFile)
|
|
293
|
+
await new Promise((resolve, reject) => {
|
|
294
|
+
const promises: Promise<void>[] = []
|
|
295
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call */
|
|
296
|
+
const watcher = chokidar.watch(codeRoot, { ignoreInitial: false })
|
|
297
|
+
watcher
|
|
298
|
+
.on('add', (p: string) => {
|
|
299
|
+
const normalized = normalizePath(p)
|
|
300
|
+
if (normalized) {
|
|
301
|
+
promises.push(handleFile(normalized))
|
|
302
|
+
}
|
|
303
|
+
})
|
|
304
|
+
.on('unlink', (p: string) => {
|
|
305
|
+
const normalized = normalizePath(p)
|
|
306
|
+
if (normalized) removeEntry(normalized)
|
|
307
|
+
})
|
|
308
|
+
.on('ready', () => {
|
|
309
|
+
Promise.all(promises)
|
|
310
|
+
.then(() => {
|
|
311
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
312
|
+
if (!enableWatch) return watcher.close()
|
|
313
|
+
return null
|
|
314
|
+
})
|
|
315
|
+
.then(resolve)
|
|
316
|
+
.catch(reject)
|
|
317
|
+
})
|
|
318
|
+
/* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call */
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// init component list before run
|
|
323
|
+
compiler.hooks.beforeRun.tapPromise(PLUGIN_NAME, async () => {
|
|
324
|
+
await searchCodeRoot(false)
|
|
325
|
+
})
|
|
326
|
+
compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, async () => {
|
|
327
|
+
await searchCodeRoot(true)
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
// rewrite component entry paths
|
|
331
|
+
compiler.resolverFactory.hooks.resolver.for('normal').tap(PLUGIN_NAME, (resolver) => {
|
|
332
|
+
resolver.hooks.result.tap(PLUGIN_NAME, (data) => {
|
|
333
|
+
if (data.path === false) return data
|
|
334
|
+
const absPath = data.path
|
|
335
|
+
const extName = path.extname(absPath)
|
|
336
|
+
if (extName === '.js' || extName === '.ts') {
|
|
337
|
+
const relPath = normalizePath(absPath)
|
|
338
|
+
if (relPath && compInfoMap[relPath.slice(0, -3)]) {
|
|
339
|
+
const redirected = `${absPath.slice(0, -3)}.glass-easel-component`
|
|
340
|
+
if ((data.context as { issuer?: string })?.issuer !== redirected) {
|
|
341
|
+
data.path = redirected
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return data
|
|
346
|
+
})
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
// collect virtual files
|
|
350
|
+
const virtualModules = this.virtualModules
|
|
351
|
+
virtualModules.apply(compiler)
|
|
352
|
+
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
353
|
+
// add loaders
|
|
354
|
+
NormalModule.getCompilationHooks(compilation).beforeLoaders.tap(
|
|
355
|
+
PLUGIN_NAME,
|
|
356
|
+
(loaders, mod) => {
|
|
357
|
+
const absPath = mod.resource
|
|
358
|
+
const extName = path.extname(absPath)
|
|
359
|
+
if (extName === '.wxml' || extName === '.wxss') {
|
|
360
|
+
const relPath = path.relative(codeRoot, absPath).split(path.sep).join('/')
|
|
361
|
+
const compPath = relPath.slice(0, -extName.length)
|
|
362
|
+
if (extName === '.wxss') {
|
|
363
|
+
loaders.forEach((x) => {
|
|
364
|
+
if (x.loader === GlassEaselMiniprogramWxssLoader) {
|
|
365
|
+
x.options = {
|
|
366
|
+
classPrefix: styleSheetManager.getScopeName(compPath),
|
|
367
|
+
relPath,
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
})
|
|
371
|
+
} else if (extName === '.wxml') {
|
|
372
|
+
loaders.forEach((x) => {
|
|
373
|
+
if (x.loader === GlassEaselMiniprogramWxmlLoader) {
|
|
374
|
+
x.options = {
|
|
375
|
+
addTemplate(content: string) {
|
|
376
|
+
wxmlContentMap[compPath] = content
|
|
377
|
+
depsTmplGroup.addTmpl(compPath, content)
|
|
378
|
+
const deps = depsTmplGroup
|
|
379
|
+
.getDirectDependencies(compPath)
|
|
380
|
+
.concat(depsTmplGroup.getScriptDependencies(compPath))
|
|
381
|
+
return {
|
|
382
|
+
compPath,
|
|
383
|
+
deps,
|
|
384
|
+
codeRoot,
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
})
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
// do some rebuild after all module compilation done
|
|
396
|
+
compilation.hooks.finishModules.tapPromise(PLUGIN_NAME, async (modules) => {
|
|
397
|
+
const tasks: Promise<any>[] = []
|
|
398
|
+
let indexModule: NormalModule | undefined
|
|
399
|
+
const tmplGroup = new TmplGroup()
|
|
400
|
+
|
|
401
|
+
// collect compilation results
|
|
402
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
403
|
+
for (const m of modules) {
|
|
404
|
+
if (m.type !== 'javascript/auto') continue
|
|
405
|
+
const module = m as NormalModule
|
|
406
|
+
if (module.resource === `${codeRoot}/index.js`) {
|
|
407
|
+
indexModule = module
|
|
408
|
+
continue
|
|
409
|
+
}
|
|
410
|
+
const absPath = module.resource
|
|
411
|
+
const extName = path.extname(absPath)
|
|
412
|
+
if (extName === '.wxml') {
|
|
413
|
+
if (module.loaders.some((x) => x.loader === GlassEaselMiniprogramWxmlLoader)) {
|
|
414
|
+
// check wxml results, read cache if needed
|
|
415
|
+
const relPath = path.relative(codeRoot, absPath).split(path.sep).join('/')
|
|
416
|
+
const compPath = relPath.slice(0, -extName.length)
|
|
417
|
+
// eslint-disable-next-line no-loop-func
|
|
418
|
+
tasks.push(
|
|
419
|
+
(async () => {
|
|
420
|
+
let content = wxmlContentMap[compPath]
|
|
421
|
+
if (content) {
|
|
422
|
+
// TODO use compiled content as better cache
|
|
423
|
+
await writeCache(compiler, relPath, content)
|
|
424
|
+
} else {
|
|
425
|
+
const s = await readCache<string>(compiler, relPath)
|
|
426
|
+
if (s === undefined) {
|
|
427
|
+
throw new Error(
|
|
428
|
+
`Cannot find WXML compilation result for ${relPath} (webpack cache broken?)`,
|
|
429
|
+
)
|
|
430
|
+
}
|
|
431
|
+
content = s
|
|
432
|
+
}
|
|
433
|
+
if (typeof content === 'string') {
|
|
434
|
+
tmplGroup.addTmpl(compPath, content)
|
|
435
|
+
}
|
|
436
|
+
})(),
|
|
437
|
+
)
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// write index module
|
|
443
|
+
await Promise.all(tasks)
|
|
444
|
+
await new Promise<void>((resolve) => {
|
|
445
|
+
updateVirtualIndexFile(tmplGroup)
|
|
446
|
+
tmplGroup.free()
|
|
447
|
+
compilation.rebuildModule(indexModule!, () => resolve())
|
|
448
|
+
})
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
// add virtual component js file
|
|
452
|
+
const updateComponentJsFile = (compPath: string) => {
|
|
453
|
+
const compInfo = compInfoMap[compPath]!
|
|
454
|
+
const scopeName = styleSheetManager.getScopeName(compPath)
|
|
455
|
+
const scopeNameStr = scopeName === undefined ? undefined : `'${scopeName}'`
|
|
456
|
+
const jsonSrcPath = path.join(codeRoot, `${compPath}.json`)
|
|
457
|
+
const wxmlSrcPath = path.join(codeRoot, `${compPath}.wxml`)
|
|
458
|
+
const wxssSrcPath = path.join(codeRoot, `${compPath}.wxss`)
|
|
459
|
+
const addStyleSheet = compInfo.hasWxss
|
|
460
|
+
? `
|
|
461
|
+
require('${escapeJsString(wxssSrcPath)}')
|
|
462
|
+
codeSpace.addStyleSheet(
|
|
463
|
+
'${escapeJsString(compPath)}',
|
|
464
|
+
'${escapeJsString(compPath)}',
|
|
465
|
+
${scopeNameStr}
|
|
466
|
+
)
|
|
467
|
+
`
|
|
468
|
+
: ''
|
|
469
|
+
virtualModules.writeModule(
|
|
470
|
+
path.join(codeRoot, `${compPath}.glass-easel-component`),
|
|
471
|
+
`
|
|
472
|
+
var index = require('${escapeJsString(codeRoot)}/index.js')
|
|
473
|
+
var codeSpace = index.codeSpace
|
|
474
|
+
var staticConfig = require('${escapeJsString(jsonSrcPath)}')
|
|
475
|
+
staticConfig.usingComponents = Object.assign(
|
|
476
|
+
{},
|
|
477
|
+
index.globalUsingComponents,
|
|
478
|
+
staticConfig.usingComponents
|
|
479
|
+
)
|
|
480
|
+
codeSpace.addComponentStaticConfig('${escapeJsString(compPath)}', staticConfig)
|
|
481
|
+
codeSpace.addCompiledTemplate('${escapeJsString(compPath)}', {
|
|
482
|
+
groupList: index.genObjectGroups,
|
|
483
|
+
content: index.genObjectGroups[require('${escapeJsString(wxmlSrcPath)}')]
|
|
484
|
+
})
|
|
485
|
+
${addStyleSheet}
|
|
486
|
+
codeSpace.globalComponentEnv(index.globalObject, '${escapeJsString(compPath)}', () => {
|
|
487
|
+
require('./${escapeJsString(path.basename(compInfo.main))}')
|
|
488
|
+
})
|
|
489
|
+
`,
|
|
490
|
+
)
|
|
491
|
+
}
|
|
492
|
+
Object.keys(compInfoMap).forEach((compPath) => updateComponentJsFile(compPath))
|
|
493
|
+
|
|
494
|
+
// add virtual index file
|
|
495
|
+
const updateVirtualIndexFile = (tmplGroup?: TmplGroup) => {
|
|
496
|
+
const entryHeader = `
|
|
497
|
+
var adapter = require('glass-easel-miniprogram-adapter')
|
|
498
|
+
var glassEasel = adapter.glassEasel
|
|
499
|
+
var env = new adapter.MiniProgramEnv()
|
|
500
|
+
exports.env = env
|
|
501
|
+
var codeSpace = env.createCodeSpace('', true)
|
|
502
|
+
codeSpace.addStyleSheet('app', 'app')
|
|
503
|
+
exports.codeSpace = codeSpace
|
|
504
|
+
exports.genObjectGroups = ${tmplGroup ? tmplGroup.getTmplGenObjectGroups() : '{}'}
|
|
505
|
+
exports.globalUsingComponents = ${JSON.stringify(globalStaticConfig.usingComponents)}
|
|
506
|
+
exports.globalObject = (function () {
|
|
507
|
+
if (typeof this !== 'undefined') { return this }
|
|
508
|
+
if (typeof globalThis !== 'undefined') { return globalThis }
|
|
509
|
+
if (typeof self !== 'undefined') { return self }
|
|
510
|
+
if (typeof window !== 'undefined') { return window }
|
|
511
|
+
if (typeof global !== 'undefined') { return global }
|
|
512
|
+
throw new Error('The global object cannot be recognized')
|
|
513
|
+
})()
|
|
514
|
+
`
|
|
515
|
+
const entryFooter = `
|
|
516
|
+
var initWithBackend = function (backend) {
|
|
517
|
+
var ab = env.associateBackend(backend)
|
|
518
|
+
;(${styleSheetManager.toCodeString()})(ab)
|
|
519
|
+
return ab
|
|
520
|
+
}
|
|
521
|
+
exports.initWithBackend = initWithBackend
|
|
522
|
+
var registerGlobalEventListener = function (backend) {
|
|
523
|
+
backend.onEvent((target, type, detail, options) => {
|
|
524
|
+
glassEasel.triggerEvent(target, type, detail, options)
|
|
525
|
+
})
|
|
526
|
+
}
|
|
527
|
+
exports.registerGlobalEventListener = registerGlobalEventListener
|
|
528
|
+
`
|
|
529
|
+
const bootstrap = this.customBootstrap
|
|
530
|
+
? ''
|
|
531
|
+
: `
|
|
532
|
+
var backend = new glassEasel.CurrentWindowBackendContext()
|
|
533
|
+
registerGlobalEventListener(backend)
|
|
534
|
+
var ab = initWithBackend(backend)
|
|
535
|
+
var root = ab.createRoot('glass-easel-root', codeSpace, '${escapeJsString(
|
|
536
|
+
this.defaultEntry,
|
|
537
|
+
)}')
|
|
538
|
+
var placeholder = document.createElement('span')
|
|
539
|
+
document.body.appendChild(placeholder)
|
|
540
|
+
root.attach(document.body, placeholder)
|
|
541
|
+
`
|
|
542
|
+
const entries = Object.values(compInfoMap).map((compInfo) => compInfo.main)
|
|
543
|
+
if (appEntry) entries.unshift(appEntry)
|
|
544
|
+
virtualModules.writeModule(
|
|
545
|
+
path.join(codeRoot, 'index.js'),
|
|
546
|
+
entryHeader +
|
|
547
|
+
entries.map((p) => `require('./${escapeJsString(p)}')\n`).join('') +
|
|
548
|
+
entryFooter +
|
|
549
|
+
bootstrap,
|
|
550
|
+
)
|
|
551
|
+
}
|
|
552
|
+
updateVirtualIndexFile()
|
|
553
|
+
|
|
554
|
+
// copy res files
|
|
555
|
+
compilation.hooks.additionalAssets.tapPromise(PLUGIN_NAME, async () => {
|
|
556
|
+
await Promise.all(
|
|
557
|
+
Object.keys(resPathMap).map(async (p) => {
|
|
558
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
|
559
|
+
compilation.assets[p] = new RawSource(await fs.readFile(path.join(codeRoot, p)))
|
|
560
|
+
}),
|
|
561
|
+
)
|
|
562
|
+
})
|
|
563
|
+
})
|
|
564
|
+
}
|
|
565
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glass-easel-miniprogram-webpack-plugin",
|
|
3
3
|
"description": "The webpack plugin of the glass-easel project for MiniProgram file structure",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/wechat-miniprogram/glass-easel.git"
|
|
@@ -17,17 +17,18 @@
|
|
|
17
17
|
"homepage": "https://github.com/wechat-miniprogram/glass-easel",
|
|
18
18
|
"main": "index.js",
|
|
19
19
|
"scripts": {
|
|
20
|
+
"build": "tsc -p .",
|
|
20
21
|
"lint": "eslint src/**/*.ts"
|
|
21
22
|
},
|
|
22
23
|
"peerDependencies": {
|
|
23
|
-
"glass-easel": "0.
|
|
24
|
-
"glass-easel-miniprogram-adapter": "0.
|
|
25
|
-
"webpack": "^5.
|
|
24
|
+
"glass-easel": "0.3.0",
|
|
25
|
+
"glass-easel-miniprogram-adapter": "0.3.0",
|
|
26
|
+
"webpack": "^5.85.0"
|
|
26
27
|
},
|
|
27
28
|
"dependencies": {
|
|
28
29
|
"chokidar": "^3.5.3",
|
|
29
|
-
"glass-easel-stylesheet-compiler": "0.
|
|
30
|
-
"glass-easel-template-compiler": "0.
|
|
30
|
+
"glass-easel-stylesheet-compiler": "0.3.0",
|
|
31
|
+
"glass-easel-template-compiler": "0.3.0",
|
|
31
32
|
"source-map": "^0.7.4",
|
|
32
33
|
"webpack-sources": "^3.2.1",
|
|
33
34
|
"webpack-virtual-modules": "^0.5.0"
|
package/tsconfig.json
ADDED
package/wxml_loader.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
const path = require('path')
|
|
1
2
|
const { escapeJsString } = require('./helpers')
|
|
2
3
|
|
|
3
4
|
module.exports = function (src, prevMap, meta) {
|
|
4
|
-
const {
|
|
5
|
+
const { addTemplate } = this.query
|
|
6
|
+
const { compPath, deps, codeRoot } = addTemplate(src, this.currentModule)
|
|
7
|
+
const requires = deps.map((x) => {
|
|
8
|
+
const p = path.join(codeRoot, `${x}.wxml`)
|
|
9
|
+
return `require('${escapeJsString(p)}');`
|
|
10
|
+
})
|
|
5
11
|
return `
|
|
6
|
-
|
|
12
|
+
${requires.join('')}
|
|
7
13
|
module.exports = '${escapeJsString(compPath)}'
|
|
8
14
|
`
|
|
9
15
|
}
|