-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathwebpack-widget.js
373 lines (350 loc) · 9.92 KB
/
webpack-widget.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
const fs = require('fs')
const path = require('path')
const CopyPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ZipPlugin = require('zip-webpack-plugin')
const GenerateWidgetHash = require('./webpack-generate-widget-hash')
const showdown = require('showdown')
converter = new showdown.Converter()
// creators and players may reference materia core files directly
// To do so rather than hard-coding the actual location of those files
// the build process will replace those references with the current relative paths to those files
const packagedJSPath = 'src="../../../js/$3"'
const devServerJSPath = 'src="/materia-assets/js/$3"'
const isRunningDevServer = process.env.NODE_ENV !== "production";
console.log("Mode: " + process.env.NODE_ENV);
const replaceTarget = isRunningDevServer ? devServerJSPath : packagedJSPath
// common paths used here
const srcPath = path.join(process.cwd(), 'src') + path.sep
const outputPath = path.join(process.cwd(), 'build') + path.sep
// list of supported browsers for use in autoprefixer
const browserList = [
'Explorer >= 11',
'last 3 Chrome versions',
'last 3 ChromeAndroid versions',
'last 3 Android versions',
'last 3 Firefox versions',
'last 3 FirefoxAndroid versions',
'last 3 iOS versions',
'last 3 Safari versions',
'last 3 Edge versions'
]
// when copying files, always ignore these
const copyIgnore = [
'.gitkeep'
]
// regex rules needed for replacing scripts loaded from materia
const materiaJSReplacements = [
{ search: /src=(\\?("|')?)(materia.enginecore.js)(\\?("|')?)/g, replace: replaceTarget },
{ search: /src=(\\?("|')?)(materia.scorecore.js)(\\?("|')?)/g, replace: replaceTarget },
{ search: /src=(\\?("|')?)(materia.creatorcore.js)(\\?("|')?)/g, replace: replaceTarget },
{ search: /src=(\\?("|')?)(materia.scorecore.js)(\\?("|')?)/g, replace: replaceTarget },
];
// webpack entries
const getDefaultEntries = () => ({
'creator': [
`${srcPath}creator.html`,
`${srcPath}creator.js`,
`${srcPath}creator.scss`
],
'player': [
`${srcPath}player.html`,
`${srcPath}player.js`,
`${srcPath}player.scss`
]
})
// Load the materia configuration settings from the package.json file
const configFromPackage = () => {
let packagePath = path.join(process.cwd(), 'package.json')
let packageJson = require(packagePath)
return {
cleanName : packageJson.materia.cleanName.toLowerCase(),
}
}
// Provides a default config option
const combineConfig = (extras = {}) => {
const rules = getDefaultRules()
const orderedRules = [
rules.loaderDoNothingToJs,
rules.loaderCompileCoffee,
rules.copyImages,
rules.loadHTMLAndReplaceMateriaScripts,
rules.loadAndPrefixSASS,
rules.reactLoader
]
const pkgConfig = configFromPackage()
const config = {
cleanName: pkgConfig.cleanName,
copyList: extras.copyList ? extras.copyList : getDefaultCopyList(),
entries: extras.entries ? extras.entries : getDefaultEntries(),
moduleRules: extras.moduleRules ? extras.moduleRules : orderedRules
}
return config
}
// list of files and directories to copy into widget
const getDefaultCopyList = () => {
const copyList = [
{
from: `${srcPath}demo.json`,
to: `${outputPath}demo.json`,
},
{
from: `${srcPath}install.yaml`,
to: outputPath,
},
{
from: `${srcPath}_icons`,
to: `${outputPath}img`,
toType: 'dir'
},
{
from: `${srcPath}_score`,
to: `${outputPath}_score-modules`,
toType: 'dir'
},
{
from: `${srcPath}_screen-shots`,
to: `${outputPath}img/screen-shots`,
toType: 'dir'
},
{
from: `${srcPath}_screen-shots`,
to: `${outputPath}img/screen-shots`,
toType: 'dir'
}
]
// assets directory is built in , but optional
let assetsPath = `${srcPath}assets`
if (fs.existsSync(assetsPath)) {
copyList.push({
from: assetsPath,
to: `${outputPath}assets`,
toType: 'dir'
})
}
return copyList
}
// Rules needed for common builds
const getDefaultRules = () => ({
// process react files (.js and .jsx)
reactLoader: {
test: /\.(js|jsx)$/i,
exclude: /node_modules/,
resolve: {
extensions: ['.js', '.jsx']
},
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-react', // transform jsx into js
'@babel/preset-env' // enable latest JS features
]
}
}
]
},
// process regular javascript files
// SKIPS the default webpack Javascript functionality
// that evaluates js code and processes module imports
loaderDoNothingToJs: {
test: /\.js$/i,
exclude: /node_modules|_guides|guides/,
type: 'javascript/auto'
},
// process coffee files by translating them to js
// SKIPS the default webpack Javascript functionality
// that evaluates js code and processes module imports
loaderCompileCoffee: {
test: /\.coffee$/i,
exclude: /node_modules/,
type: 'javascript/auto',
use: [
{
loader: 'coffee-loader',
options: {
transpile:{
presets: [
'@babel/preset-env'
]
}
}
}
],
},
// webpack is going to look at all the images, fonts, etc
// in the src of the html files, this will tell webpack
// how to deal with those files
copyImages: {
test: /\.(jpe?g|png|gif|svg|ico|ttf|eot|woff|woff2)$/i,
exclude: /node_modules/,
type: 'asset/resource',
generator: {
filename: '[name][ext]'
}
},
// Loads the html files and minifies their contents
// Rewrites the paths to our materia core libs provided by materia server
//
loadHTMLAndReplaceMateriaScripts: {
test: /\.html$/i,
exclude: /node_modules|_guides|guides/,
type: 'asset/source',
use: [
{
loader: 'string-replace-loader',
options: { multiple: materiaJSReplacements }
},
]
},
// Process SASS/SCSS/CSS Files
// Adds autoprefixer
loadAndPrefixSASS: {
test: /\.s(a|c)ss|css$/i,
exclude: /node_modules\/(?!(materia-widget-development-kit\/templates)\/).*/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: "css-loader",
options: {
url: false,
esModule: false
},
},
{
// postcss-loader is needed to run autoprefixer
loader: 'postcss-loader',
options: {
postcssOptions: {
// add autoprefixer, tell it what to prefix
plugins: [
require('autoprefixer')({
overrideBrowserslist: browserList
})
],
}
},
},
"sass-loader",
],
}
})
// This is a base config for building legacy widgets
// It will skip webpack's javascript functionality
// to avoid having to make changes to the source code of those widgets
// the config argument allows you to override some settings
// you can update the return from this method to modify or alter
// the base configuration
const getLegacyWidgetBuildConfig = (config = {}) => {
// load and combine the config
let cfg = combineConfig(config)
let maphash = new Map();
// This is here to account for the current entry scheme in widgets, until it gets replaced
for (const [key, value] of Object.entries(cfg.entries))
{
let name = key;
let valueSplit = value[0].split('.');
let valueExt = valueSplit.length > 1 ? valueSplit[valueSplit.length - 1] : '';
// If the first value in entry is not an HTML file, then we won't create an HtmlWebpackPlugin for it
if (valueExt != "html")
{
continue;
}
else if (!maphash.has(name))
{
maphash.set(name, new HtmlWebpackPlugin({
filename: `${name}.html`,
template: `${value[0].split('.')[0]}.html`,
inject: false,
minify: false,
chunks: [`${name}`]
}));
}
}
let htmlWebpackPlugins = Array.from(maphash.values());
if (fs.existsSync(`${srcPath}_guides/creator.md`) || fs.existsSync(`${srcPath}_guides/player.md`))
{
cfg.copyList.unshift(
{
from: `./node_modules/materia-widget-development-kit/templates/guideStyles.css`,
to: `${outputPath}guides/guideStyles.css`
})
}
if (fs.existsSync(`${srcPath}_guides/creator.md`))
{
let md = fs.readFileSync(`${srcPath}_guides/creator.md`, 'utf-8');
let html = converter.makeHtml(md);
htmlWebpackPlugins.unshift(
new HtmlWebpackPlugin({
chunks: [],
template: './node_modules/materia-widget-development-kit/templates/guide-template.hbs',
filename: `${outputPath}/guides/creator.html`,
htmlContent: html,
htmlTitle: 'Widget Guide'
})
)
}
if (fs.existsSync(`${srcPath}_guides/player.md`))
{
let md = fs.readFileSync(`${srcPath}_guides/player.md`, 'utf-8');
let html = converter.makeHtml(md);
htmlWebpackPlugins.unshift(
new HtmlWebpackPlugin({
chunks: [],
template: './node_modules/materia-widget-development-kit/templates/guide-template.hbs',
filename: `${outputPath}/guides/player.html`,
htmlContent: html,
htmlTitle: 'Widget Guide'
})
)
}
let build = {
mode: process.env.NODE_ENV == 'production' ? 'production' : 'development',
stats: {children: false},
devtool: process.env.NODE_ENV == 'production' ? false : 'eval-source-map',
entry: cfg.entries,
// write files to the outputPath (default = ./build) using the object keys from 'entry' above
output: {
path: outputPath,
filename: '[name].js',
publicPath: '',
clean: true
},
module: {rules: cfg.moduleRules},
plugins: [
// clear the build directory
// copy all the common resources to the build directory
new CopyPlugin({
patterns: cfg.copyList,
}),
...htmlWebpackPlugins,
// extract css from the webpack output
new MiniCssExtractPlugin({
filename: '[name].css'
}),
// zip everything in the build path to zip dir
new ZipPlugin({
path: `${outputPath}_output`,
filename: cfg.cleanName,
extension: 'wigt'
}),
new GenerateWidgetHash({
widget: `_output/${cfg.cleanName}.wigt`,
output: `_output/${cfg.cleanName}-build-info.yml`
})
]
}
return build
}
module.exports = {
materiaJSReplacements: materiaJSReplacements,
configFromPackage: configFromPackage,
getLegacyWidgetBuildConfig: getLegacyWidgetBuildConfig,
getDefaultRules: getDefaultRules,
getDefaultCopyList: getDefaultCopyList,
getDefaultEntries: getDefaultEntries
}