Skip to content

Commit 508bebb

Browse files
committed
bugfix(mjml-browser): yarn build-browser
the build browser command was failing. This fixes it.
1 parent a8fe155 commit 508bebb

File tree

7 files changed

+1143
-100
lines changed

7 files changed

+1143
-100
lines changed

packages/mjml-browser/README.md

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,79 @@
1-
## MJML Browser build
1+
# MJML Browser
22

3-
This package allows MJML to be used client-side.
3+
Browser build of MJML - convert MJML to responsive HTML in the browser.
44

5-
### Usage
5+
## Build
66

7-
It can be used as the regular mjml package :
7+
```bash
8+
yarn build
9+
```
10+
11+
This creates `index.js` - a UMD bundle that can be used in any browser.
12+
13+
## Usage
14+
15+
### In Browser (UMD)
16+
17+
```html
18+
<!doctype html>
19+
<html>
20+
<body>
21+
<div id="output"></div>
22+
23+
<script src="path/to/mjml-browser/index.js"></script>
24+
<script>
25+
const mjmlTemplate = `
26+
<mjml>
27+
<mj-body>
28+
<mj-section>
29+
<mj-column>
30+
<mj-text>Hello World!</mj-text>
31+
</mj-column>
32+
</mj-section>
33+
</mj-body>
34+
</mjml>
35+
`
36+
37+
// mjml returns a Promise
38+
mjml(mjmlTemplate).then((result) => {
39+
console.log(result.html)
40+
document.getElementById('output').innerHTML = result.html
41+
})
42+
</script>
43+
</body>
44+
</html>
45+
```
46+
47+
### With Module Bundler
848

949
```javascript
10-
var mjml2html = require('mjml-browser')
50+
const mjml2html = require('mjml-browser')
51+
52+
// Returns a Promise
53+
mjml2html(mjmlString, options).then((result) => {
54+
console.log(result.html)
55+
})
1156

12-
var result = mjml2html(mjml, options)
57+
// Or with async/await
58+
const result = await mjml2html(mjmlString, options)
59+
console.log(result.html)
1360
```
1461

15-
### Unavailable features
62+
## Unavailable Features
63+
64+
- `mj-include` tags are unavailable and will be ignored
65+
- Features involving the `.mjmlconfig` file are unavailable (no custom components)
66+
- HTML minification is disabled (htmlnano is stubbed out)
67+
- File system operations are not available
68+
69+
## API
1670

17-
- `mj-include` tags are unavailable and will be ignored.
18-
- features involving the `.mjmlconfig` file are unavailable, which means no custom components.
71+
The function returns a Promise that resolves to an object with:
72+
73+
```javascript
74+
{
75+
html: string, // The generated HTML
76+
errors: array, // Array of validation errors
77+
json: object // MJML structure as JSON
78+
}
79+
```

packages/mjml-browser/index.js

Lines changed: 822 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/mjml-browser/package.json

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,44 @@
11
{
22
"name": "mjml-browser",
3+
"version": "5.0.0-alpha.4",
4+
"license": "MIT",
35
"description": "MJML: the only framework that makes responsive-email easy",
4-
"version": "5.0.0-alpha.6",
5-
"main": "lib/index.js",
6-
"files": [
7-
"lib"
6+
"keywords": [
7+
"email",
8+
"mjml",
9+
"responsive"
810
],
9-
"repository": {
10-
"type": "git",
11-
"url": "git+https://github.com/mjmlio/mjml.git",
12-
"directory": "packages/mjml-browser"
13-
},
14-
"license": "MIT",
15-
"bugs": {
16-
"url": "https://github.com/mjmlio/mjml/issues"
17-
},
18-
"homepage": "https://mjml.io",
11+
"author": "Maxime",
1912
"scripts": {
20-
"clean": "rimraf lib",
2113
"build": "webpack"
2214
},
15+
"dependencies": {
16+
"@babel/plugin-proposal-class-properties": "^7.18.6",
17+
"@babel/plugin-proposal-logical-assignment-operators": "^7.20.7",
18+
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
19+
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
20+
"@babel/plugin-proposal-private-methods": "^7.18.6",
21+
"assert": "^2.1.0",
22+
"browserify-zlib": "^0.2.0",
23+
"buffer": "^6.0.3",
24+
"crypto-browserify": "^3.12.0",
25+
"events": "^3.3.0",
26+
"mjml": "^5.0.0-alpha.4",
27+
"path-browserify": "^1.0.1",
28+
"process": "^0.11.10",
29+
"querystring-es3": "^0.2.1",
30+
"stream-browserify": "^3.0.0",
31+
"stream-http": "^3.2.0",
32+
"url": "^0.11.4",
33+
"util": "^0.12.5"
34+
},
2335
"devDependencies": {
24-
"@babel/cli": "^7.8.4",
25-
"@babel/core": "^7.8.4",
26-
"@babel/plugin-proposal-class-properties": "^7.8.3",
27-
"@babel/plugin-proposal-decorators": "^7.8.3",
28-
"@babel/plugin-proposal-export-default-from": "^7.8.3",
29-
"@babel/plugin-proposal-function-bind": "^7.8.3",
30-
"@babel/preset-env": "^7.8.4",
31-
"babel-loader": "^8.0.6",
32-
"rimraf": "^3.0.2",
33-
"uglifyjs-webpack-plugin": "^2.1.3",
34-
"webpack": "^4.36.1",
35-
"webpack-cli": "^3.3.6"
36+
"@babel/core": "^7.25.9",
37+
"@babel/preset-env": "^7.25.9",
38+
"babel-loader": "^8.4.1",
39+
"babel-plugin-lodash": "^3.3.4",
40+
"uglifyjs-webpack-plugin": "^2.2.0",
41+
"webpack": "^4.47.0",
42+
"webpack-cli": "^3.3.12"
3643
}
3744
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {}
2+
module.exports.default = {}
3+
4+
// Stub common properties that might be accessed
5+
if (typeof global !== 'undefined') {
6+
global.process = global.process || {}
7+
global.process.env = global.process.env || {}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Mock htmlnano for browser - just returns HTML unchanged
2+
module.exports = {
3+
process: function (html, options) {
4+
return Promise.resolve({ html: html })
5+
},
6+
}
7+
8+
module.exports.default = module.exports
Lines changed: 113 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,136 @@
11
const path = require('path')
22
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
3+
const webpack = require('webpack')
34

45
module.exports = {
56
mode: 'production',
6-
entry: {
7-
"mjml": ['../mjml/lib/index'],
8-
},
9-
optimization: {
10-
minimizer: [
11-
new UglifyJsPlugin({
12-
uglifyOptions: {
13-
ecma: 5,
14-
keep_classnames: true,
15-
keep_fnames: true,
16-
compress: {
17-
passes: 2,
18-
keep_fargs: false,
19-
},
20-
output: {
21-
beautify: false,
22-
},
23-
mangle: true,
24-
},
25-
}),
26-
],
27-
},
7+
entry: path.resolve(__dirname, '../mjml/lib/index'),
288
output: {
29-
library: 'mjml',
9+
path: __dirname,
3010
filename: 'index.js',
31-
path: path.resolve(__dirname, './lib'),
11+
library: 'mjml',
3212
libraryTarget: 'umd',
3313
umdNamedDefine: true,
14+
globalObject: 'typeof self !== "undefined" ? self : this',
15+
},
16+
performance: {
17+
hints: false,
3418
},
3519
resolve: {
3620
alias: {
37-
'path': path.resolve(__dirname, 'browser-mocks/path'),
38-
'fs': path.resolve(__dirname, 'browser-mocks/fs'),
39-
'uglify-js': path.resolve(__dirname, 'browser-mocks/uglify-js'),
21+
// Existing aliases
22+
stream: require.resolve('stream-browserify'),
23+
'node:stream': require.resolve('stream-browserify'),
24+
buffer: require.resolve('buffer/'),
25+
'node:buffer': require.resolve('buffer/'),
26+
path: require.resolve('path-browserify'),
27+
28+
// Node.js core modules that need polyfills
29+
'node:crypto': require.resolve('crypto-browserify'),
30+
'node:url': require.resolve('url/'),
31+
'node:util$': require.resolve('util/'),
32+
'node:assert': require.resolve('assert/'),
33+
'node:events': require.resolve('events/'),
34+
'node:zlib': require.resolve('browserify-zlib'),
35+
'node:http': require.resolve('stream-http'),
36+
querystring: require.resolve('querystring-es3'),
37+
'node:querystring': require.resolve('querystring-es3'),
38+
39+
// Node.js modules that don't work in browser - stub them out
40+
'node:util/types': path.resolve(__dirname, 'stubs/empty.js'),
41+
'node:http2': path.resolve(__dirname, 'stubs/empty.js'),
42+
'node:async_hooks': path.resolve(__dirname, 'stubs/empty.js'),
43+
'node:net': path.resolve(__dirname, 'stubs/empty.js'),
44+
'node:tls': path.resolve(__dirname, 'stubs/empty.js'),
45+
'node:perf_hooks': path.resolve(__dirname, 'stubs/empty.js'),
46+
'node:diagnostics_channel': path.resolve(__dirname, 'stubs/empty.js'),
47+
'node:console': path.resolve(__dirname, 'stubs/empty.js'),
48+
'node:worker_threads': path.resolve(__dirname, 'stubs/empty.js'),
49+
50+
// File system modules that don't work in browser
51+
fs: path.resolve(__dirname, 'stubs/empty.js'),
52+
'fs/promises': path.resolve(__dirname, 'stubs/empty.js'),
53+
child_process: path.resolve(__dirname, 'stubs/empty.js'),
54+
module: path.resolve(__dirname, 'stubs/empty.js'),
55+
56+
// Optional dependency that's not needed
57+
typescript: path.resolve(__dirname, 'stubs/empty.js'),
58+
59+
// Problematic dependencies - use specific stubs
60+
prettier: path.resolve(__dirname, 'stubs/empty.js'),
61+
cosmiconfig: path.resolve(__dirname, 'stubs/empty.js'),
62+
htmlnano: path.resolve(__dirname, 'stubs/htmlnano.js'),
63+
64+
// Undici is not needed in browser - stub it out completely
65+
undici: path.resolve(__dirname, 'stubs/empty.js'),
4066
},
4167
},
68+
node: {
69+
// Webpack 4 way to disable Node.js polyfills
70+
fs: 'empty',
71+
child_process: 'empty',
72+
worker_threads: false,
73+
net: 'empty',
74+
tls: 'empty',
75+
},
4276
module: {
77+
exprContextCritical: false,
4378
rules: [
4479
{
45-
test: /\.js$/,
46-
exclude: path.join(__dirname, 'node_modules'),
47-
use: [
48-
{
49-
loader: 'babel-loader',
50-
options: {
51-
presets: [
80+
test: /\.(js|mjs)$/,
81+
exclude:
82+
/node_modules\/(?!(cheerio|htmlparser2|domhandler|dom-serializer|domelementtype|domutils|entities|parse5|encoding-sniffer)\/).*/,
83+
use: {
84+
loader: 'babel-loader',
85+
options: {
86+
presets: [
87+
[
5288
'@babel/preset-env',
89+
{
90+
targets: { browsers: ['> 0.25%', 'not dead'] },
91+
},
5392
],
54-
plugins: [
55-
["@babel/plugin-proposal-decorators", { "legacy": true }],
56-
["@babel/plugin-proposal-class-properties", { "loose" : true }],
57-
"@babel/plugin-proposal-function-bind",
58-
"@babel/plugin-proposal-export-default-from",
59-
],
60-
babelrc: false,
61-
},
93+
],
94+
plugins: [
95+
'lodash',
96+
'@babel/plugin-proposal-class-properties',
97+
'@babel/plugin-proposal-private-methods',
98+
'@babel/plugin-proposal-optional-chaining',
99+
'@babel/plugin-proposal-nullish-coalescing-operator',
100+
'@babel/plugin-proposal-logical-assignment-operators',
101+
],
62102
},
63-
],
103+
},
64104
},
65-
],
105+
],
106+
},
107+
optimization: {
108+
minimizer: [
109+
new UglifyJsPlugin({
110+
parallel: true,
111+
sourceMap: false,
112+
uglifyOptions: {
113+
compress: {
114+
drop_console: false,
115+
},
116+
output: {
117+
comments: false,
118+
},
119+
},
120+
}),
121+
],
66122
},
123+
plugins: [
124+
new webpack.DefinePlugin({
125+
'process.env.NODE_ENV': JSON.stringify('production'),
126+
'process.env': JSON.stringify({}),
127+
'process.version': JSON.stringify('v18.0.0'),
128+
'process.versions.node': JSON.stringify('18.0.0'),
129+
'process.platform': JSON.stringify('browser'),
130+
}),
131+
new webpack.ProvidePlugin({
132+
process: 'process/browser',
133+
Buffer: ['buffer', 'Buffer'],
134+
}),
135+
],
67136
}

0 commit comments

Comments
 (0)