diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index ed0ec7dd2..e818f04c0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -19,14 +19,14 @@ A clear and concise description of what you expected to happen. **MJML environment (please complete the following information):** - OS: [e.g. MacOS] - - MJML Version [e.g. 4.2.0] + - MJML Version [e.g. 5.x.x] - MJML tool used [e.g MJML App] **Email sending environment(for rendering issues)**: - - Platform used to send the email [e.g [Putsmail](https://putsmail.com/)] + - Platform used to send the email [e.g [Putsmail](https://putsmail.com/)] **Affected email clients (for rendering issues):** - - Email Client [e.g Gmail] + - Email Client [e.g Gmail] - OS: [e.g. Windows] - Browser [e.g. Google Chrome] diff --git a/.github/workflows/mjml-workflow.yml b/.github/workflows/mjml-workflow.yml index 707cc43d9..90bb55039 100644 --- a/.github/workflows/mjml-workflow.yml +++ b/.github/workflows/mjml-workflow.yml @@ -1,11 +1,11 @@ name: Mjml CI -on: [push, pull_request] +on: [pull_request] jobs: build: runs-on: ubuntu-latest strategy: matrix: - node-version: [10.x, 12.x, 14.x] + node-version: [16.x, 18.x] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} @@ -13,7 +13,7 @@ jobs: with: node-version: ${{ matrix.node-version }} - name: Run linting & tests - run: | + run: | yarn install yarn build yarn lint diff --git a/README.md b/README.md index 2b3341157..7472af338 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ -# MJML 4 +# MJML 5 If you're looking for MJML 3.3.X check [this branch](https://github.com/mjmlio/mjml/tree/3.3.x) +If you're looking for MJML 4.14.X check [this branch](https://github.com/mjmlio/mjml/tree/4.14.x)

@@ -96,12 +97,11 @@ You can pass optional `arguments` to the CLI and combine them. argument | description | default value ---------|--------|-------------- -`mjml -m [input]` | Migrates a v3 MJML file to the v4 syntax | NA `mjml [input] -o [output]` | Writes the output to [output] | NA `mjml [input] -s` | Writes the output to `stdout` | NA `mjml -w [input]` | Watches the changes made to `[input]` (file or folder) | NA -`mjml [input] --config.beautify` | Beautifies the output (`true` or `false`) | true -`mjml [input] --config.minify` | Minifies the output (`true` or `false`) | false +`mjml [input] --config.beautify` | Beautifies the output (`true` or `false`) requires js-beautify in package.json | true +`mjml [input] --config.minify` | Minifies the output (`true` or `false`) requires html-minifier in package.json | false See [mjml-cli documentation](https://github.com/mjmlio/mjml/blob/master/packages/mjml-cli/README.md) for more information about config options. diff --git a/doc/install.md b/doc/install.md index c59f1f041..172def945 100644 --- a/doc/install.md +++ b/doc/install.md @@ -7,6 +7,14 @@ head over to Usage for other ways to use MJML. npm install mjml ``` +Note that `html-minifier` and `js-beautify` become fully optional in MJML 5. + +You can add them to your `package.json` if you need to. + +```bash +npm install html-minifier js-beautify +``` + # Development To work on MJML, make changes and create merge requests, download and @@ -40,7 +48,7 @@ MJML comes with an ecosystem of tools and plugins, check out: - [Atom plugin](https://atom.io/users/mjmlio) (MJML needs to be installed separately) - [Sublime Text plugin](https://packagecontrol.io/packages/MJML-syntax) (MJML needs to be installed separately) -For more information, check the [Tooling](#tooling) section. +For more information, check the [Tooling](#tooling) section. For more tools, check the [Community](https://mjml.io/community) page. ## Command line interface diff --git a/packages/mjml-cli/package.json b/packages/mjml-cli/package.json index efe24df18..2f9849b33 100644 --- a/packages/mjml-cli/package.json +++ b/packages/mjml-cli/package.json @@ -28,11 +28,8 @@ "@babel/runtime": "^7.14.6", "chokidar": "^3.0.0", "glob": "^7.1.1", - "html-minifier": "^4.0.0", - "js-beautify": "^1.6.14", "lodash": "^4.17.21", "mjml-core": "4.14.1", - "mjml-migrate": "4.14.1", "mjml-parser-xml": "4.14.1", "mjml-validator": "4.13.0", "yargs": "^16.1.0" diff --git a/packages/mjml-cli/src/client.js b/packages/mjml-cli/src/client.js index 87c3db9c4..15ef30e52 100644 --- a/packages/mjml-cli/src/client.js +++ b/packages/mjml-cli/src/client.js @@ -2,11 +2,8 @@ import path from 'path' import yargs from 'yargs' import { flow, pick, isNil, negate, pickBy } from 'lodash/fp' import { isArray, isEmpty, map, get, omit } from 'lodash' -import { html as htmlBeautify } from 'js-beautify' -import { minify as htmlMinify } from 'html-minifier' import mjml2html, { components, initializeType } from 'mjml-core' -import migrate from 'mjml-migrate' import validate, { dependencies } from 'mjml-validator' import MJMLParser from 'mjml-parser-xml' @@ -20,14 +17,6 @@ import outputToConsole from './commands/outputToConsole' import { version as cliVersion } from '../package.json' import DEFAULT_OPTIONS from './helpers/defaultOptions' -const beautifyConfig = { - indent_size: 2, - wrap_attributes_indent_size: 2, - max_preserve_newline: 0, - preserve_newlines: false, - end_with_newline: true, -} - const minifyConfig = { collapseWhitespace: true, minifyCSS: false, @@ -60,11 +49,6 @@ export default async () => { describe: 'Compile MJML File(s)', type: 'array', }, - m: { - alias: 'migrate', - describe: 'Migrate MJML3 File(s) (deprecated)', - type: 'array', - }, v: { alias: 'validate', describe: 'Run validator on File(s)', @@ -197,8 +181,6 @@ export default async () => { watchFiles(inputFiles, { ...argv, config, - minifyConfig, - beautifyConfig, }) KEEP_OPEN = true break @@ -216,9 +198,6 @@ export default async () => { try { let compiled switch (inputOpt) { - case 'm': - compiled = { html: migrate(i.mjml, { beautify: true }) } - break case 'v': // eslint-disable-next-line no-case-declarations const mjmlJson = MJMLParser(i.mjml, { components, @@ -242,16 +221,10 @@ export default async () => { ...omit(config, ['minify', 'beautify']), filePath: filePath || i.file, actualPath: i.file, + beautify, + minify, + minifyOptions: minifyConfig, }) - if (beautify) { - compiled.html = htmlBeautify(compiled.html, beautifyConfig) - } - if (minify) { - compiled.html = htmlMinify(compiled.html, { - ...minifyConfig, - ...config.minifyOptions, - }) - } } } @@ -322,7 +295,11 @@ export default async () => { } case 's': { const addFileHeaderComment = !argv.noStdoutFileComment - Promise.all(convertedStream.map(converted => outputToConsole(converted, addFileHeaderComment))) + Promise.all( + convertedStream.map((converted) => + outputToConsole(converted, addFileHeaderComment), + ), + ) .then(() => (process.exitCode = EXIT_CODE)) // eslint-disable-line no-return-assign .catch(() => (process.exitCode = 1)) // eslint-disable-line no-return-assign break diff --git a/packages/mjml-cli/src/commands/watchFiles.js b/packages/mjml-cli/src/commands/watchFiles.js index 09033c87d..ea39f001a 100644 --- a/packages/mjml-cli/src/commands/watchFiles.js +++ b/packages/mjml-cli/src/commands/watchFiles.js @@ -5,8 +5,6 @@ import path from 'path' import mjml2html from 'mjml-core' import { flow, pickBy, flatMap, uniq, difference, remove } from 'lodash/fp' import { omit } from 'lodash' -import { html as htmlBeautify } from 'js-beautify' -import { minify as htmlMinify } from 'html-minifier' import readFile from './readFile' import makeOutputToFile from './outputToFile' @@ -51,7 +49,7 @@ export default (input, options) => { const readAndCompile = flow( (file) => ({ file, content: readFile(file).mjml }), (args) => { - const { config, beautifyConfig, minifyConfig } = options + const { config, minifyConfig } = options const beautify = config.beautify && config.beautify !== 'false' const minify = config.minify && config.minify !== 'false' @@ -59,16 +57,10 @@ export default (input, options) => { filePath: args.file, actualPath: args.file, ...omit(config, ['minify', 'beautify']), + minifyConfig, + minify, + beautify, }) - if (beautify) { - compiled.html = htmlBeautify(compiled.html, beautifyConfig) - } - if (minify) { - compiled.html = htmlMinify(compiled.html, { - ...minifyConfig, - ...config.minifyOptions, - }) - } return { ...args, diff --git a/packages/mjml-core/package.json b/packages/mjml-core/package.json index cc122aa56..94f962959 100644 --- a/packages/mjml-core/package.json +++ b/packages/mjml-core/package.json @@ -25,11 +25,8 @@ "@babel/runtime": "^7.14.6", "cheerio": "1.0.0-rc.12", "detect-node": "^2.0.4", - "html-minifier": "^4.0.0", - "js-beautify": "^1.6.14", "juice": "^9.0.0", "lodash": "^4.17.21", - "mjml-migrate": "4.14.1", "mjml-parser-xml": "4.14.1", "mjml-validator": "4.13.0" }, diff --git a/packages/mjml-core/src/index.js b/packages/mjml-core/src/index.js index 192949488..6f93d66ac 100644 --- a/packages/mjml-core/src/index.js +++ b/packages/mjml-core/src/index.js @@ -12,8 +12,6 @@ import { } from 'lodash' import path from 'path' import juice from 'juice' -import { html as htmlBeautify } from 'js-beautify' -import { minify as htmlMinify } from 'html-minifier' import { load } from 'cheerio' import MJMLParser from 'mjml-parser-xml' @@ -21,7 +19,6 @@ import MJMLValidator, { dependencies as globalDependencies, assignDependencies, } from 'mjml-validator' -import { handleMjml3 } from 'mjml-migrate' import { initComponent } from './createComponent' import globalComponents, { @@ -116,7 +113,6 @@ export default function mjml2html(mjml, options = {}) { validationLevel = 'soft', filePath = '.', actualPath = '.', - noMigrateWarn = false, preprocessors, presets = [], printerSupport = false, @@ -146,8 +142,6 @@ export default function mjml2html(mjml, options = {}) { }) } - mjml = handleMjml3(mjml, { noMigrateWarn }) - const globalData = { backgroundColor: '', beforeDoctype: '', @@ -186,7 +180,7 @@ export default function mjml2html(mjml, options = {}) { if (errors.length > 0) { throw new ValidationError( `ValidationError: \n ${errors - .map(e => e.formattedMessage) + .map((e) => e.formattedMessage) .join('\n')}`, errors, ) @@ -227,7 +221,7 @@ export default function mjml2html(mjml, options = {}) { } } - const applyAttributes = mjml => { + const applyAttributes = (mjml) => { const parse = (mjml, parentMjClass = '') => { const { attributes, tagName, children } = mjml const classes = get(mjml.attributes, 'mj-class', '').split(' ') @@ -272,7 +266,7 @@ export default function mjml2html(mjml, options = {}) { globalAttributes: { ...globalData.defaultAttributes['mj-all'], }, - children: map(children, mjml => parse(mjml, nextParentMjClass)), + children: map(children, (mjml) => parse(mjml, nextParentMjClass)), } } @@ -293,7 +287,7 @@ export default function mjml2html(mjml, options = {}) { addComponentHeadSyle(headStyle) { globalData.componentsHeadStyle.push(headStyle) }, - setBackgroundColor: color => { + setBackgroundColor: (color) => { globalData.backgroundColor = color }, processing: (node, context) => processing(node, context, applyAttributes), @@ -344,12 +338,12 @@ export default function mjml2html(mjml, options = {}) { if (mjOutsideRaws.length) { const toAddBeforeDoctype = mjOutsideRaws.filter( - elt => + (elt) => elt.attributes.position && elt.attributes.position === 'file-start', ) if (toAddBeforeDoctype.length) { globalData.beforeDoctype = toAddBeforeDoctype - .map(elt => elt.content) + .map((elt) => elt.content) .join('\n') } } @@ -396,31 +390,43 @@ export default function mjml2html(mjml, options = {}) { content = mergeOutlookConditionnals(content) if (beautify) { - // eslint-disable-next-line no-console - console.warn( - '"beautify" option is deprecated in mjml-core and only available in mjml cli.', - ) - content = htmlBeautify(content, { - indent_size: 2, - wrap_attributes_indent_size: 2, - max_preserve_newline: 0, - preserve_newlines: false, - }) + try { + // eslint-disable-next-line global-require, import/no-extraneous-dependencies, import/no-unresolved + const htmlBeautify = require('js-beautify') + + content = htmlBeautify(content, { + indent_size: 2, + wrap_attributes_indent_size: 2, + max_preserve_newline: 0, + preserve_newlines: false, + end_with_newline: true, + }) + } catch (e) { + // eslint-disable-next-line no-console + console.warn( + 'js-beautify is now an optional library to reduce bundle size. To continue using it please do npm install js-beautify / yarn add js-beautify', + ) + } } if (minify) { - // eslint-disable-next-line no-console - console.warn( - '"minify" option is deprecated in mjml-core and only available in mjml cli.', - ) - - content = htmlMinify(content, { - collapseWhitespace: true, - minifyCSS: false, - caseSensitive: true, - removeEmptyAttributes: true, - ...minifyOptions, - }) + try { + // eslint-disable-next-line global-require, import/no-extraneous-dependencies, import/no-unresolved + const htmlMinify = require('html-minifier') + + content = htmlMinify(content, { + collapseWhitespace: true, + minifyCSS: false, + caseSensitive: true, + removeEmptyAttributes: true, + ...minifyOptions, + }) + } catch (e) { + // eslint-disable-next-line no-console + console.warn( + 'html-minifier is now an optional library as it has some potential ReDos Attack. To continue using it please do npm install html-minifier / yarn add html-minifier', + ) + } } return { diff --git a/packages/mjml-migrate/README.md b/packages/mjml-migrate/README.md index fe4d5fadc..b56a1b90a 100644 --- a/packages/mjml-migrate/README.md +++ b/packages/mjml-migrate/README.md @@ -1,4 +1,4 @@ -# mjml-migrate +# mjml-migrate (Archived) ## Purpose @@ -17,5 +17,5 @@ Clone the repo & `npm install` or install via NPM: `npm install mjml-migrate` * `mj-container` is removed and its attributes are passed to `mj-body` * Unitless values are converted to `px` * `mj-social`'s syntax is replaced with the v4 syntax -* Unsupported tags (defined in `unavailableTags` in `config.js`) are removed +* Unsupported tags (defined in `unavailableTags` in `config.js`) are removed diff --git a/packages/mjml-validator/src/MJMLRulesCollection.js b/packages/mjml-validator/src/MJMLRulesCollection.js index 5e0e8f659..1238cedf3 100644 --- a/packages/mjml-validator/src/MJMLRulesCollection.js +++ b/packages/mjml-validator/src/MJMLRulesCollection.js @@ -14,6 +14,7 @@ const MJMLRulesCollection = { export function registerRule(rule, name) { if (typeof rule !== 'function') { + // eslint-disable-next-line no-console return console.error('Your rule must be a function') } diff --git a/packages/mjml-validator/src/dependencies.js b/packages/mjml-validator/src/dependencies.js index 1c867f82f..70a30b299 100644 --- a/packages/mjml-validator/src/dependencies.js +++ b/packages/mjml-validator/src/dependencies.js @@ -16,10 +16,12 @@ export const assignDependencies = (target, ...sources) => { } target[tag] = Array.from(new Set(list)) } else { + // eslint-disable-next-line no-console console.warn('dependency "tag" must be of type string') } } } else { + // eslint-disable-next-line no-console console.warn('"dependencies" must be an object.') } } diff --git a/packages/mjml-validator/src/index.js b/packages/mjml-validator/src/index.js index e0c12c5ec..420512780 100644 --- a/packages/mjml-validator/src/index.js +++ b/packages/mjml-validator/src/index.js @@ -20,6 +20,7 @@ export default function MJMLValidator(element, options = {}) { const skipElements = options.skipElements || SKIP_ELEMENTS if (options.dependencies == null) { + // eslint-disable-next-line no-console console.warn('"dependencies" option should be provided to mjml validator') } diff --git a/packages/mjml/README.md b/packages/mjml/README.md index 772867d57..114fee619 100644 --- a/packages/mjml/README.md +++ b/packages/mjml/README.md @@ -1,4 +1,4 @@ -# MJML 4 +# MJML 5

@@ -133,7 +133,7 @@ minifyOptions | Options for html minifier, see [mjml-cli documentation](https:// mjmlConfigPath | string | The path or directory of the `.mjmlconfig` file (for custom components use) | `process.cwd()` useMjmlConfigOptions | Allows to use the `options` attribute from `.mjmlconfig` file | false -Note that it's also possible to define preprocessors in your mjmlconfig file. For this, you need to use a `.mjmlconfig.js` file. This js file needs to export an Object with the same structure as a standard JSON .mjmlconfig file. +Note that it's also possible to define preprocessors in your mjmlconfig file. For this, you need to use a `.mjmlconfig.js` file. This js file needs to export an Object with the same structure as a standard JSON .mjmlconfig file. ## API diff --git a/packages/mjml/package.json b/packages/mjml/package.json index 934939dfa..98e388b61 100644 --- a/packages/mjml/package.json +++ b/packages/mjml/package.json @@ -29,7 +29,6 @@ "@babel/runtime": "^7.14.6", "mjml-cli": "4.14.1", "mjml-core": "4.14.1", - "mjml-migrate": "4.14.1", "mjml-preset-core": "4.14.1", "mjml-validator": "4.13.0" }, diff --git a/readme-ja.md b/readme-ja.md index 274995e07..11092ff76 100644 --- a/readme-ja.md +++ b/readme-ja.md @@ -1,6 +1,7 @@ -# MJML 4 +# MJML 5 もしも、MJML 3.3.Xについて探しているのであれば、[このブランチ](https://github.com/mjmlio/mjml/tree/3.3.x)をご確認ください。 +もしも、MJML 4.14.Xについて探しているのであれば、[このブランチ](https://github.com/mjmlio/mjml/tree/4.14.x)をご確認ください。