├── .eslintignore ├── .gitattributes ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .publishrc ├── .travis.yml ├── CHANGES.md ├── LICENSE ├── README.md ├── bin └── cli.js ├── index.js ├── jsconfig.json ├── lib └── identifier.js ├── package.json ├── test ├── _input.js ├── _output.js └── index.js └── transforms ├── config-m-route ├── _input.js ├── _output.js └── index.js ├── controller ├── _input.js ├── _output.js └── index.js ├── m-component ├── _input.js ├── _output.js └── index.js ├── m-module ├── _input.js ├── _output.js └── index.js ├── m-mount-vnodes ├── _input.js ├── _output.js └── index.js ├── m-route-get-set ├── _input.js ├── _output.js └── index.js ├── m-route-mode ├── _input.js ├── _output.js └── index.js ├── m-route-vnodes ├── _input.js ├── _output.js └── index.js ├── m-start-end-computation ├── _input.js ├── _output.js └── index.js ├── m-sync ├── _input.js ├── _output.js └── index.js ├── query-string ├── _input.js ├── _output.js └── index.js ├── svg-namespace-xlink ├── _input.js ├── _output.js └── index.js ├── unsafe-component-onunload ├── _input.js ├── _output.js └── index.js ├── unsafe-component-wrapping ├── _input.js ├── _output.js └── index.js ├── unsafe-config ├── _input.js ├── _output.js └── index.js ├── unsafe-event-cancel-redraw ├── _input.js ├── _output.js └── index.js ├── unsafe-view ├── _input.js ├── _output.js └── index.js ├── warning-m-deferred ├── _input.js ├── _output.js └── index.js ├── warning-m-prop ├── _input.js ├── _output.js └── index.js ├── warning-m-redraw-arg ├── _input.js ├── _output.js └── index.js ├── warning-m-redraw-strategy ├── _input.js ├── _output.js └── index.js └── warning-prevent-unload ├── _input.js ├── _output.js └── index.js /.eslintignore: -------------------------------------------------------------------------------- 1 | _output.js 2 | _input.js 3 | test/ 4 | coverage/ 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # So NPM publishes from windows don't screw things up 5 | bin/ text eol=lf 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Original Source 2 | 3 | ```js 4 | // SOURCE 5 | ``` 6 | 7 | ## Tool Output 8 | 9 | ```js 10 | // OUTPUT 11 | ``` 12 | 13 | ## Expected Output 14 | 15 | ```js 16 | // EXPECTED 17 | ``` 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directories 27 | node_modules/ 28 | jspm_packages 29 | 30 | # Optional npm cache directory 31 | .npm/ 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | 36 | # vscode directory 37 | .vscode/ 38 | 39 | # ========================= 40 | # Operating System Files 41 | # ========================= 42 | 43 | # OSX 44 | # ========================= 45 | 46 | .DS_Store 47 | .AppleDouble 48 | .LSOverride 49 | 50 | # Thumbnails 51 | ._* 52 | 53 | # Files that might appear in the root of a volume 54 | .DocumentRevisions-V100 55 | .fseventsd 56 | .Spotlight-V100 57 | .TemporaryItems 58 | .Trashes 59 | .VolumeIcon.icns 60 | 61 | # Directories potentially created on remote AFP share 62 | .AppleDB 63 | .AppleDesktop 64 | Network Trash Folder 65 | Temporary Items 66 | .apdisk 67 | 68 | # Windows 69 | # ========================= 70 | 71 | # Windows image file caches 72 | Thumbs.db 73 | ehthumbs.db 74 | 75 | # Folder config file 76 | Desktop.ini 77 | 78 | # Recycle Bin used on file shares 79 | $RECYCLE.BIN/ 80 | 81 | # Windows Installer files 82 | *.cab 83 | *.msi 84 | *.msm 85 | *.msp 86 | 87 | # Windows shortcuts 88 | *.lnk 89 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Mac crap 12 | .DS_Store 13 | 14 | # Folders 15 | temp/ 16 | coverage/ 17 | test/ 18 | 19 | # Random Stuff 20 | .gitattributes 21 | .npmignore 22 | .travis.yml 23 | .eslintignore 24 | .publishrc 25 | jsconfig.json 26 | -------------------------------------------------------------------------------- /.publishrc: -------------------------------------------------------------------------------- 1 | { 2 | "validations": { 3 | "vulnerableDependencies": false, 4 | "uncommittedChanges": true, 5 | "untrackedFiles": true, 6 | "sensitiveData": true, 7 | "branch": "master", 8 | "gitTag": true 9 | }, 10 | "confirm": true, 11 | "publishTag": "latest", 12 | "prePublishScript": "npm test" 13 | } 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - node 5 | - '6' 6 | - '4' 7 | 8 | cache: 9 | directories: 10 | - node_modules 11 | 12 | deploy: 13 | provider: npm 14 | email: npm@patcavit.com 15 | api_key: 16 | secure: UQ4zU3nyGxd+DxV2SLhiinAxBckvlJOfBDZbLyYDTeUxEQMrn6vgyNyqs/R6h9JLIYpNsCaMiHcWK89JHl7idVaY6dymRGYFIQn98CFINrfKUPPUyW/LyQhAa2xWX1sLOx7E39/WipK40ISKoFlzbxFsHafnsEXSdHzxEcaRecv5S/doW3+Nn2+Ya+9/46hGhmzHvdA/+nv61NbiCCOthaHH4Mn8yaW3MoEy/N2SBDDNhYkj2Tpu05ZhDFGOZWqNYg7upHhQuSul3BEdT6agj0XCC7jy1X2rNLdfRO9Gv9NQUgA8rHBlV9vnp13ZY9+FfBvnfbTN2n7oinnIYfDj5h8FmB5HXeurOsXsST8kyT3bfjn7hTb8yjCmrUnXMv7JTCvXNgIONepf2sLi8qdluH2+PZ5vCS1/bGeioGFVQJVReP9p0zOe0IS1yEMtBWvxF7K1DF9ER3SJBR14sA1EyuMifuyuhP5Y6+jcSd6B7sIvVD3oerzjYKAqvFxSgr5u73PeOrcw6093ikLSxUldBS8q3RV3J0TiadCt4Cs9x20+PTjuuJmUr7d6QnJhe0GebP7i5YpksntHmH/dwF/X4tWJF5kK7ZolVIzDx1jfrGAkRvfkzavpAT2U6VyamrLs4UQNP4oMaUEJznIcUkQNi8cp42cMwzJA+ikURI/nAcA= 17 | on: 18 | tags: true 19 | repo: tivac/mithril-codemods 20 | branch: master 21 | node: 'node' 22 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## 3.5.2 4 | 5 | - feat: controller.onunload transform should search more aggressively 6 | 7 | ## 3.5.1 8 | 9 | - docs: document latest change 10 | 11 | ## 3.5.0 12 | 13 | - feat: m.route.buildQueryString/parseQueryString rewritten to m.buildQueryString/parseQueryString (#30) 14 | 15 | ## 3.4.3 16 | 17 | - fix: handle more than just m("...") in raw vnode rule 18 | - fix: always rename controller properties to oninit (#25) 19 | 20 | ## 3.4.2 21 | 22 | - fix: don't re-apply view(ctrl) -> view(vnode) transform 23 | 24 | ## 3.4.1 25 | 26 | - docs: use mithril.js.org for links 27 | 28 | ## 3.4.0 29 | 30 | - docs: Readme formatting (#24) 31 | - feat: controller.onunload rewritten to controller.onremove (#23) 32 | 33 | ## 3.3.1 34 | 35 | - fix: Expressions, not statements for m.route() 36 | 37 | ## 3.3.0 38 | 39 | - refactor: use console.warn() instead of comments 40 | - refactor: always run warnings last 41 | - feat: warn about m.redraw.strategy() 42 | - refactor: remove m.route.param rewriting 43 | 44 | ## 3.2.1 45 | 46 | - chore: set up travis publishing 47 | - test: travis tests on node 6/7/4 48 | 49 | ## 3.2.0 50 | 51 | - docs: change-log added 52 | - fix: support more m.route.param() types 53 | 54 | ## 3.1.0 55 | 56 | - Alignment tweak 57 | - Remove unecessary protections 58 | - Not impossible to warn about, fortunately 59 | - Warn about instances of m.deferred 60 | - Sync up w/ work done 61 | - Make sure every transform tracks stats 62 | - Strip m.startComputation/m.endComputation 63 | - Fix mithril documentation links 64 | - Clean up 65 | - Add missing transforms, tweak formatting 66 | - Add issue template 67 | 68 | ## 3.0.1 69 | 70 | - Don't really care about vulns for a CLI tool 71 | 72 | ## 3.0.0 73 | 74 | - Better exclusions 75 | - Warnings 76 | - m.sync -> Promise.all 77 | - Formatting 78 | - Add coverage, rename ospec to tests 79 | - Unused 80 | - Clean up NPM package contents 81 | - execa@0.5.0 82 | - Make package.json match what's on NPM 83 | - Badges! :pager: 84 | - Remove m.request.run stuff 85 | - More arrow-functiony 86 | - m.sync -> Promise.all 87 | - Formatting 88 | 89 | ## 2.0.2 90 | 91 | - Readme updates 92 | 93 | ## 2.0.1 94 | 95 | - ESLint 96 | 97 | ## 2.0.0 98 | 99 | - Bring back safe/unsafe, tweak CLI 100 | - Expand view rewriting to fn(ctrl) 101 | - Clean up event cancelling 102 | - Rework upwards() usage & features 103 | - Use "function" for arrow support 104 | - Use "Function" type in case arrows are involved 105 | - Safer replacement via identifier.replace 106 | - Strip empty config functions after transform 107 | - Try & handle more config init cases (#17) 108 | - Add migration doc link 109 | - Prepend svg element href attr w/ xlink namespace (#16) 110 | - Rename first param in controller functions to vnode (#15) 111 | - Doc update & transform planning 112 | 113 | ## 1.1.2 114 | 115 | - Attempt to fix bin line-endings 116 | - Make m.route.get/set transform safer (#14) 117 | 118 | ## 1.1.1 119 | 120 | - Take advantage of matchNode/j.match (#9) 121 | 122 | ## 1.1.0 123 | 124 | - Fix lint issues 125 | - Add view transform (no migration docs?!?) 126 | - view function rewriting (#7) 127 | - LTS & stable only 128 | - Clean up filtering a bit 129 | - Tests trim input to disparity (just in case) 130 | - Support m.module and use oncreate instead of oninit (#5) (Rasmus Porsager) 131 | 132 | ## 1.0.2 133 | 134 | - Remove a false-positive 135 | - Fix bad check 136 | - Robustify 137 | - Robustify 138 | 139 | ## 1.0.1 140 | 141 | - Minor cleanups 142 | 143 | ## 1.0.0 144 | 145 | - Initial release 146 | 147 | 148 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Pat Cavit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mithril-codemods [![NPM Version](https://img.shields.io/npm/v/mithril-codemods.svg)](https://www.npmjs.com/package/mithril-codemods) [![NPM License](https://img.shields.io/npm/l/mithril-codemods.svg)](https://www.npmjs.com/package/mithril-codemods) 2 | ================ 3 | [![NPM Downloads](https://img.shields.io/npm/dm/mithril-codemods.svg)](https://www.npmjs.com/package/mithril-codemods) 4 | [![Build Status](https://img.shields.io/travis/MithrilJS/mithril-codemods.svg)](https://travis-ci.org/MithrilJS/mithril-codemods) 5 | [![Dependency Status](https://img.shields.io/david/MithrilJS/mithril-codemods.svg)](https://david-dm.org/MithrilJS/mithril-codemods) 6 | [![devDependency Status](https://img.shields.io/david/dev/MithrilJS/mithril-codemods.svg)](https://david-dm.org/MithrilJS/mithril-codemods#info=devDependencies) 7 | 8 | Automated porting of `mithril@0.2` code to `mithril@1.0`. 9 | 10 | ## Usage 11 | 12 | ``` 13 | > npm i -g mithril-codemods 14 | > mithril-codemods --help 15 | 16 | Usage 17 | $ mithril-codemods [ ...] 18 | 19 | Options 20 | --unsafe, -u Use unsafe transforms 21 | --apply, -a Apply transforms (instead of a dry run) 22 | 23 | Examples 24 | mithril-codemods **/*.js 25 | mithril-codemods --apply **/*.js 26 | mithril-codemods -ua some/specific/file.js 27 | ``` 28 | 29 | ## Transforms 30 | 31 | ### Safe 32 | 33 | These transforms are pretty safe and unlikely to have many false positives. 34 | 35 | - [Replace `m.component()` with `m()`](http://mithril.js.org/change-log.html#mcomponent-removed) 36 | - [Rename `controller`️ to `oninit`](http://mithril.js.org/change-log.html#component-controller-function) 37 | - [Rename `m.route.mode` to `m.route.prefix()` and adjust args](http://mithril.js.org/change-log.html#mroutemode) 38 | - [Rename `m.route()` to `m.route.get()` and `m.route("route")` to `m.route.set("route")`](http://mithril.js.org/change-log.html#readingwriting-the-current-route) 39 | - [Replace `config: m.route` ️w️i️t️h️ `oncreate: m.route.link`](http://mithril.js.org/change-log.html#mroute-and-anchor-tags) 40 | - [Wrap raw vnodes in `m.mount()`/`m.route()`](http://mithril.js.org/change-log.html#passing-vnodes-to-mmount-and-mroute) 41 | - [Replace `options` with `vnode.attrs`](http://mithril.js.org/change-log.html#component-arguments) 42 | - [Add `xlink` namespacing to ``](http://mithril.js.org/change-log.html#xlink-namespace-required) 43 | - [Replace `m.sync` with `Promise.all`](http://mithril.js.org/change-log.html#msync-removed) 44 | - [Remove `m.startComputation`/`m.endComputation`](http://mithril.js.org/change-log.html#no-more-redraw-locks) 45 | - [Replace `m.route.build/parseQueryString` with `m.build/parseQueryString`](http://mithril.js.org/change-log.html#buildingparsing-query-strings) 46 | 47 | ### ⚠️️️ Unsafe ⚠️ 48 | 49 | These transform are usually fine, but not applied by default since they can have unfortunate side-effects. 50 | 51 | - [Convert `m.redraw.strategy("none")` to `e.redraw = false`](http://mithril.js.org/change-log.html#cancelling-redraw-from-event-handlers) 52 | - [Wrap unwrapped components](http://mithril.js.org/change-log.html#passing-components-to-m) 53 | - [Replace `config` with `oninit`/`onupdate`](http://mithril.js.org/change-log.html#config-function) 54 | - [Rewrite `view(ctrl, options)` as `view(vnode)`](http://mithril.js.org/change-log.html#view-parameters) 55 | 56 | ### Warnings 57 | 58 | There are certain classes of changes that are impossible to automatically convert. 59 | 60 | - [`m.prop` removed](http://mithril.js.org/change-log.html#mprop-removed) 61 | - [`m.redraw(true)` removed](http://mithril.js.org/change-log.html#synchronous-redraw-removed) 62 | - [`m.deferred()` removed](http://mithril.js.org/change-log.html#mdeferred-removed) 63 | - [`onunload` preventing unmounting](http://mithril.js.org/change-log.html#preventing-unmounting) 64 | -------------------------------------------------------------------------------- /bin/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* eslint no-console:"off" */ 3 | "use strict"; 4 | 5 | var path = require("path"), 6 | 7 | meow = require("meow"), 8 | execa = require("execa"), 9 | globby = require("globby"), 10 | series = require("promise-map-series"), 11 | 12 | types = require("../"), 13 | 14 | cli = meow(` 15 | Usage 16 | $ mithril-codemods [ ...] 17 | 18 | Options 19 | --unsafe, -u Use unsafe transforms 20 | --apply, -a Apply transforms (instead of a dry run) 21 | 22 | Examples 23 | mithril-codemods **/*.js 24 | mithril-codemods --apply **/*.js 25 | mithril-codemods -ua some/specific/file.js 26 | `, { 27 | boolean : [ "unsafe", "apply" ], 28 | alias : { 29 | a : "apply", 30 | u : "unsafe" 31 | } 32 | }), 33 | 34 | transforms = types.safe; 35 | 36 | if(!cli.input.length) { 37 | cli.showHelp(0); 38 | 39 | return; 40 | } 41 | 42 | if(cli.flags.unsafe) { 43 | transforms = transforms.concat(types.unsafe); 44 | } 45 | 46 | transforms = transforms.concat(types.warning); 47 | 48 | globby(cli.input).then((paths) => 49 | series( 50 | transforms, 51 | (transform) => { 52 | console.log(`${transform.name} running`); 53 | 54 | return execa( 55 | path.resolve(__dirname, "../node_modules/.bin/jscodeshift"), 56 | [ "-t", transform.file, cli.flags.apply ? "" : "-d" ].concat(paths), 57 | { stdio : "inherit" } 58 | ) 59 | .then((result) => console.log(`${result.stdout}\n`)) 60 | .catch(console.error.bind(console)); 61 | } 62 | ) 63 | ); 64 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var fs = require("fs"), 4 | path = require("path"), 5 | 6 | dirs = fs.readdirSync(path.join(__dirname, "/transforms")), 7 | 8 | prefixes = [ 9 | "unsafe", 10 | "warning" 11 | ], 12 | 13 | out = {}; 14 | 15 | // Split based on name 16 | dirs.forEach((dir) => { 17 | var prefix = dir.split("-")[0], 18 | 19 | transform = { 20 | name : dir, 21 | file : path.join(__dirname, `./transforms/${dir}/index.js`) 22 | }; 23 | 24 | 25 | if(prefixes.indexOf(prefix) === -1) { 26 | prefix = "safe"; 27 | } 28 | 29 | if(!out[prefix]) { 30 | out[prefix] = []; 31 | } 32 | 33 | return out[prefix].push(transform); 34 | }); 35 | 36 | module.exports = out; 37 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=759670 3 | // for the documentation about the jsconfig.json format 4 | "compilerOptions": { 5 | "target": "es6", 6 | "module": "commonjs" 7 | }, 8 | "exclude": [ 9 | "node_modules" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /lib/identifier.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // Update references to child identifiers 4 | exports.replace = function(j, root, find, replace) { 5 | j(root) 6 | .find(j.Identifier, { name : find }) 7 | // Ensure that `arg` is the object being modified 8 | // Means that fooga. will be ignored, but .fooga will be modified 9 | .filter((p2) => (j.MemberExpression.check(p2.parent.node) ? 10 | p2.parent.get("object") === p2 : 11 | true 12 | )) 13 | .replaceWith(replace); 14 | }; 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mithril-codemods", 3 | "version": "3.5.2", 4 | "description": "Transform mithril@0.2.x code into mithril@1.x using jscodeshift", 5 | "main": "index.js", 6 | "bin": "./bin/cli.js", 7 | "scripts": { 8 | "cover": "istanbul cover --print both test", 9 | "commitmsg": "validate-commit-msg", 10 | "lint": "eslint .", 11 | "preview": "changes || true", 12 | "test": "node test", 13 | "posttest": "npm run lint", 14 | "preversion": "npm test", 15 | "version": "changes", 16 | "postversion": "git push --follow-tags" 17 | }, 18 | "repository": "tivac/mithril-codemod", 19 | "keywords": [ 20 | "mithril", 21 | "codemod", 22 | "jscodeshift" 23 | ], 24 | "author": "Pat Cavit ", 25 | "license": "MIT", 26 | "dependencies": { 27 | "execa": "^0.6.0", 28 | "globby": "^6.0.0", 29 | "jscodeshift": "^0.3.28", 30 | "meow": "^3.7.0", 31 | "promise-map-series": "^0.2.3", 32 | "to-js-identifier": "^1.0.0" 33 | }, 34 | "devDependencies": { 35 | "@studio/changes": "^1.1.0", 36 | "disparity": "^2.0.0", 37 | "eslint": "^3.15.0", 38 | "eslint-config-arenanet": "^3.3.0", 39 | "husky": "^0.13.1", 40 | "istanbul": "^0.4.5", 41 | "ospec": "^1.0.0", 42 | "validate-commit-msg": "^2.8.2" 43 | }, 44 | "eslintConfig": { 45 | "extends": "arenanet", 46 | "env": { 47 | "node": true 48 | }, 49 | "rules": { 50 | "newline-per-chained-call": "off", 51 | "indent": "off" 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/_input.js: -------------------------------------------------------------------------------- 1 | // m-module 2 | m.module(document.body, { 3 | // controller 4 | controller : function(options) { 5 | var ctrl = this; 6 | 7 | this.field = options.field; 8 | 9 | // m-route-get-set 10 | this.route = m.route() || ""; 11 | 12 | // query-string 13 | this.query = m.route.parseQueryString(query); 14 | 15 | this.route = function(route) { 16 | // m-route-get-set 17 | m.route(route); 18 | 19 | // query-string 20 | ctrl.query = m.route.buildQueryString({ foo : 1 }); 21 | }; 22 | 23 | this.computation = function() { 24 | // m-start-end-computation 25 | m.startComputation(); 26 | 27 | // m-sync 28 | m.sync([ 29 | doSomeWork(), 30 | doSomeOtherWork() 31 | ]) 32 | // m-start-end-computation 33 | .then(() => { 34 | m.endComputation() 35 | }); 36 | }; 37 | 38 | // unsafe-component-onunload 39 | ctrl.onunload = onunload; 40 | }, 41 | 42 | // unsafe-view 43 | view : function(ctrl, options) { 44 | return m("div", 45 | m("a", { 46 | // config-m-route 47 | config : m.route 48 | }), 49 | 50 | // m-component 51 | m.component(Component), 52 | m.component(Component2, { arg : 1 }), 53 | 54 | // svg-namespace-xlink 55 | m("svg", 56 | m("use[href='svg-attr.svg#icon']", { 57 | // unsafe-config 58 | config : function(el, isInit) { 59 | if(!isInit) { 60 | "oncreate"; 61 | } 62 | } 63 | }) 64 | ), 65 | 66 | // unsafe-component-wrapping 67 | { 68 | view : function() { 69 | return m("div", { 70 | attr : ctrl.route, 71 | 72 | // unsafe-config 73 | config: function(el) { 74 | "onupdate"; 75 | }, 76 | 77 | // unsafe-event-cancel-redraw 78 | onclick : function() { 79 | m.redraw.strategy("none"); 80 | } 81 | }); 82 | } 83 | } 84 | ); 85 | } 86 | }); 87 | 88 | // m-mount-vnodes 89 | m.mount(document.body, m("div")); 90 | 91 | // m-route-mode 92 | m.route.mode = "pathname"; 93 | 94 | // m-route-vnodes 95 | m.route(document.body, "/", { 96 | "/" : m("div"), 97 | "/2" : m("div", { class : "class" }) 98 | }); 99 | -------------------------------------------------------------------------------- /test/_output.js: -------------------------------------------------------------------------------- 1 | // m-module 2 | m.mount(document.body, { 3 | // controller 4 | oninit : function(vnode) { 5 | var ctrl = this; 6 | 7 | this.field = vnode.attrs.field; 8 | 9 | // m-route-get-set 10 | this.route = m.route.get() || ""; 11 | 12 | // query-string 13 | this.query = m.parseQueryString(query); 14 | 15 | this.route = function(route) { 16 | // m-route-get-set 17 | m.route.set(route); 18 | 19 | // query-string 20 | ctrl.query = m.buildQueryString({ foo : 1 }); 21 | }; 22 | 23 | this.computation = function() { 24 | // m-sync 25 | Promise.all([ 26 | doSomeWork(), 27 | doSomeOtherWork() 28 | ]) 29 | .then(() => {}); 30 | }; 31 | }, 32 | 33 | // unsafe-view 34 | view : function(vnode) { 35 | return m("div", 36 | m("a", { 37 | oncreate: m.route.link 38 | }), 39 | 40 | m(Component), 41 | m(Component2, { arg : 1 }), 42 | 43 | // svg-namespace-xlink 44 | m("svg", 45 | m("use[xlink:href='svg-attr.svg#icon']", { 46 | oncreate: function(vnode) { 47 | "oncreate"; 48 | } 49 | }) 50 | ), 51 | 52 | m(// unsafe-component-wrapping 53 | { 54 | view : function() { 55 | return m("div", { 56 | attr : vnode.state.route, 57 | 58 | // unsafe-config 59 | onupdate: function(vnode) { 60 | "onupdate"; 61 | }, 62 | 63 | // unsafe-event-cancel-redraw 64 | onclick : function(e) { 65 | e.redraw = false; 66 | } 67 | }); 68 | } 69 | }) 70 | ); 71 | }, 72 | 73 | onremove: onunload 74 | }); 75 | 76 | // m-mount-vnodes 77 | m.mount(document.body, { 78 | view: function() { 79 | return m("div"); 80 | } 81 | }); 82 | 83 | m.route.prefix(""); 84 | 85 | // m-route-vnodes 86 | m.route(document.body, "/", { 87 | "/" : { 88 | view : function() { 89 | return m("div"); 90 | } 91 | }, 92 | "/2" : { 93 | view : function() { 94 | return m("div", { class : "class" }); 95 | } 96 | } 97 | }); 98 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var fs = require("fs"), 4 | 5 | o = require("ospec"), 6 | execa = require("execa"), 7 | disparity = require("disparity"), 8 | jscodeshift = require("jscodeshift"), 9 | 10 | transforms = require("../index.js"); 11 | 12 | function stats() { } 13 | 14 | o.spec("mithril-codemod", () => { 15 | Object.keys(transforms).forEach((type) => 16 | o.spec(type, () => 17 | transforms[type].forEach((t) => { 18 | var transform = require(t.file), 19 | 20 | fn = transform.only ? o.only : o; 21 | 22 | fn(t.name, () => { 23 | var input = `./transforms/${t.name}/_input.js`, 24 | result, diff; 25 | 26 | result = transform({ 27 | path : input, 28 | source : fs.readFileSync(input, "utf8") 29 | }, { 30 | jscodeshift, 31 | stats 32 | }); 33 | 34 | diff = disparity.unified( 35 | fs.readFileSync(`./transforms/${t.name}/_output.js`, "utf8").trim(), 36 | result.trim(), 37 | { 38 | paths : [ 39 | `./transforms/${t.name}/_input.js (transformed)`, 40 | `./transforms/${t.name}/_output.js` 41 | ] 42 | } 43 | ); 44 | 45 | o(diff).equals("")(`\n${diff}`); 46 | }) 47 | }) 48 | ) 49 | ); 50 | 51 | o("Multiple transforms", () => { 52 | var tasks = transforms.safe.concat(transforms.unsafe, transforms.warning), 53 | source = fs.readFileSync("./test/_input.js", "utf8"), 54 | diff; 55 | 56 | tasks.forEach((t) => { 57 | source = require(t.file)({ 58 | path : "./test/_input.js", 59 | source : source 60 | }, { 61 | jscodeshift, 62 | stats 63 | }); 64 | }); 65 | 66 | diff = disparity.unified( 67 | fs.readFileSync(`./test/_output.js`, "utf8").trim(), 68 | source.trim(), 69 | { 70 | paths : [ 71 | "./test/_input.js (transformed)", 72 | "./test/_output.js" 73 | ] 74 | } 75 | ); 76 | 77 | o(diff).equals("")(`\n${diff}`); 78 | }); 79 | }); 80 | 81 | o.run(); 82 | -------------------------------------------------------------------------------- /transforms/config-m-route/_input.js: -------------------------------------------------------------------------------- 1 | m("div", { 2 | config: m.route 3 | }); 4 | -------------------------------------------------------------------------------- /transforms/config-m-route/_output.js: -------------------------------------------------------------------------------- 1 | m("div", { 2 | oncreate: m.route.link 3 | }); 4 | -------------------------------------------------------------------------------- /transforms/config-m-route/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // http://mithril.js.org/change-log.html#mroute-and-anchor-tags 4 | // Convert `config : m.route` into `oncreate : m.route.link` 5 | module.exports = (file, api) => { 6 | var j = api.jscodeshift, 7 | s = api.stats; 8 | 9 | return j(file.source) 10 | .find(j.Property, { 11 | key : { name : "config" }, 12 | value : { 13 | object : { name : "m" }, 14 | property : { name : "route" } 15 | } 16 | }) 17 | .forEach(() => s("config: m.route")) 18 | .replaceWith(() => j.property( 19 | "init", 20 | j.identifier("oncreate"), 21 | j.template.expression`m.route.link` 22 | )) 23 | .toSource(); 24 | }; 25 | -------------------------------------------------------------------------------- /transforms/controller/_input.js: -------------------------------------------------------------------------------- 1 | m({ 2 | controller : function() { } 3 | }); 4 | 5 | m.mount(document.body, { 6 | controller : function(options) { 7 | options; 8 | options.fooga; 9 | 10 | thing.options = options; 11 | 12 | foo(options); 13 | foo(thing.options); 14 | } 15 | }); 16 | 17 | m.mount(document.body, { 18 | controller : controller 19 | }); 20 | -------------------------------------------------------------------------------- /transforms/controller/_output.js: -------------------------------------------------------------------------------- 1 | m({ 2 | oninit : function() { } 3 | }); 4 | 5 | m.mount(document.body, { 6 | oninit : function(vnode) { 7 | vnode.attrs; 8 | vnode.attrs.fooga; 9 | 10 | thing.options = vnode.attrs; 11 | 12 | foo(vnode.attrs); 13 | foo(thing.options); 14 | } 15 | }); 16 | 17 | m.mount(document.body, { 18 | oninit : controller 19 | }); 20 | -------------------------------------------------------------------------------- /transforms/controller/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var replace = require("../../lib/identifier.js").replace; 4 | 5 | // http://mithril.js.org/change-log.html#component-controller-function 6 | // Convert `controller` object properties (that are functions) to be called `oninit` instead 7 | // Change the first param to `vnode` 8 | // Convert any access to the first param to use vnode.attrs. instead 9 | module.exports = (file, api) => { 10 | var j = api.jscodeshift, 11 | s = api.stats; 12 | 13 | return j(file.source) 14 | .find(j.Property, { 15 | key : { name : "controller" } 16 | }) 17 | .forEach((p) => { 18 | s("controller property"); 19 | 20 | // update name to `oninit` 21 | p.get("key").replace(j.identifier("oninit")); 22 | 23 | // If we don't know what the value is, bail at this point 24 | if(!j.match(p, { value : j.Function.check })) { 25 | return; 26 | } 27 | 28 | // No args means we're done 29 | if(!p.get("value", "params").getValueProperty("length")) { 30 | return; 31 | } 32 | 33 | // Update references to first arg w/ `vnode.attrs` 34 | replace( 35 | j, 36 | p.get("value", "body"), 37 | p.get("value", "params", 0).getValueProperty("name"), 38 | j.template.expression`vnode.attrs` 39 | ); 40 | 41 | // Rename first arg to `vnode` 42 | p.get("value", "params", 0).replace(j.identifier("vnode")); 43 | }) 44 | .toSource(); 45 | }; 46 | -------------------------------------------------------------------------------- /transforms/m-component/_input.js: -------------------------------------------------------------------------------- 1 | m.component(component, { arg : "1" }); 2 | -------------------------------------------------------------------------------- /transforms/m-component/_output.js: -------------------------------------------------------------------------------- 1 | m(component, { arg : "1" }); 2 | -------------------------------------------------------------------------------- /transforms/m-component/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // https://mithril.js.org/change-log.html#mcomponent-removed 4 | // Convert `m.component()` invocations into just `m()` 5 | module.exports = (file, api) => { 6 | var j = api.jscodeshift, 7 | s = api.stats; 8 | 9 | return j(file.source) 10 | .find(j.CallExpression, { 11 | callee : { 12 | object : { name : "m" }, 13 | property : { name : "component" } 14 | } 15 | }) 16 | .forEach(() => s("m.component()")) 17 | .replaceWith((p) => j.template.expression` 18 | m(${p.get("arguments").value}) 19 | `) 20 | .toSource(); 21 | }; 22 | -------------------------------------------------------------------------------- /transforms/m-module/_input.js: -------------------------------------------------------------------------------- 1 | m.module(document.body, component); 2 | -------------------------------------------------------------------------------- /transforms/m-module/_output.js: -------------------------------------------------------------------------------- 1 | m.mount(document.body, component); 2 | -------------------------------------------------------------------------------- /transforms/m-module/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // https://mithril.js.org/change-log.html#mcomponent-removed 4 | // Convert `m.module()` invocations into `m.mount()` 5 | module.exports = (file, api) => { 6 | var j = api.jscodeshift, 7 | s = api.stats; 8 | 9 | return j(file.source) 10 | .find(j.CallExpression, { 11 | callee : { 12 | object : { name : "m" }, 13 | property : { name : "module" } 14 | } 15 | }) 16 | .forEach(() => s("m.module()")) 17 | .replaceWith((p) => j.template.expression` 18 | m.mount(${p.get("arguments").value}) 19 | `) 20 | .toSource(); 21 | }; 22 | -------------------------------------------------------------------------------- /transforms/m-mount-vnodes/_input.js: -------------------------------------------------------------------------------- 1 | m.mount(document.body, m("div")); 2 | -------------------------------------------------------------------------------- /transforms/m-mount-vnodes/_output.js: -------------------------------------------------------------------------------- 1 | m.mount(document.body, { 2 | view: function() { 3 | return m("div"); 4 | } 5 | }); 6 | -------------------------------------------------------------------------------- /transforms/m-mount-vnodes/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // https://mithril.js.org/change-log.html#passing-vnodes-to-mmount-and-mroute 4 | // Converts raw vnodes passed to m.mount into simple components 5 | module.exports = (file, api) => { 6 | var j = api.jscodeshift, 7 | s = api.stats; 8 | 9 | return j(file.source) 10 | .find(j.CallExpression, { 11 | callee : { 12 | object : { name : "m" }, 13 | property : { name : "mount" } 14 | }, 15 | arguments : [ 16 | {}, 17 | { 18 | 19 | callee : { name : "m" }, 20 | arguments : [ 21 | { type : "Literal" } 22 | ] 23 | } 24 | ] 25 | }) 26 | .forEach(() => s("m.mount(vnode)")) 27 | .replaceWith((p) => j.template.expression` 28 | m.mount(${p.get("arguments", 0).value}, { 29 | view: function() { 30 | return ${p.get("arguments", 1).value}; 31 | } 32 | }) 33 | `) 34 | .toSource(); 35 | }; 36 | -------------------------------------------------------------------------------- /transforms/m-route-get-set/_input.js: -------------------------------------------------------------------------------- 1 | m.route(); 2 | m.route("/new-route"); 3 | m.route(document.body, "/", {}); 4 | m.route.get(); 5 | function foo() { 6 | return (m.route() || ""); 7 | } 8 | -------------------------------------------------------------------------------- /transforms/m-route-get-set/_output.js: -------------------------------------------------------------------------------- 1 | m.route.get(); 2 | m.route.set("/new-route"); 3 | m.route(document.body, "/", {}); 4 | m.route.get(); 5 | function foo() { 6 | return (m.route.get() || ""); 7 | } 8 | -------------------------------------------------------------------------------- /transforms/m-route-get-set/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // https://mithril.js.org/change-log.html#readingwriting-the-current-route 4 | // m.route() => m.route.get(); 5 | // m.route("/new-route") => m.route.set("/new-route); 6 | module.exports = (file, api) => { 7 | var j = api.jscodeshift, 8 | s = api.stats; 9 | 10 | return j(file.source) 11 | .find(j.CallExpression, { 12 | callee : { 13 | object : { name : "m" }, 14 | property : { name : "route" } 15 | }, 16 | 17 | arguments : (node) => node.length < 2 18 | }) 19 | .forEach(() => s('m.route()/m.route("/route")')) 20 | .replaceWith((p) => ( 21 | p.get("arguments").getValueProperty("length") ? 22 | j.template.expression` 23 | m.route.set(${p.get("arguments").value}) 24 | ` : 25 | j.template.expression` 26 | m.route.get() 27 | ` 28 | ) 29 | ) 30 | .toSource(); 31 | }; 32 | -------------------------------------------------------------------------------- /transforms/m-route-mode/_input.js: -------------------------------------------------------------------------------- 1 | m.route.mode = "search"; 2 | m.route.mode = "hash"; 3 | m.route.mode = "pathname"; 4 | -------------------------------------------------------------------------------- /transforms/m-route-mode/_output.js: -------------------------------------------------------------------------------- 1 | m.route.prefix("?"); 2 | m.route.prefix("#"); 3 | m.route.prefix(""); 4 | -------------------------------------------------------------------------------- /transforms/m-route-mode/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var conversion = { 4 | search : "?", 5 | hash : "#", 6 | pathname : "" 7 | }; 8 | 9 | // https://mithril.js.org/change-log.html#mroute-mode 10 | // Converts m.route.mode = "..." calls into m.route.prefix() 11 | module.exports = (file, api) => { 12 | var j = api.jscodeshift, 13 | s = api.stats; 14 | 15 | return j(file.source) 16 | .find(j.ExpressionStatement, { 17 | expression : { 18 | operator : "=", 19 | 20 | left : { 21 | object : { 22 | object : { name : "m" }, 23 | property : { name : "route" } 24 | }, 25 | 26 | property : { name : "mode" } 27 | }, 28 | 29 | right : (node) => (node.value in conversion) 30 | } 31 | }) 32 | .forEach(() => s("m.route.mode")) 33 | .replaceWith((p) => j.template.statement` 34 | m.route.prefix(${j.literal(conversion[p.get("expression", "right").getValueProperty("value")])}); 35 | `) 36 | .toSource(); 37 | }; 38 | -------------------------------------------------------------------------------- /transforms/m-route-vnodes/_input.js: -------------------------------------------------------------------------------- 1 | m.route(document.body, "/", { 2 | "/" : m("div"), 3 | "/2" : m("div", { class : "class" }) 4 | }); 5 | -------------------------------------------------------------------------------- /transforms/m-route-vnodes/_output.js: -------------------------------------------------------------------------------- 1 | m.route(document.body, "/", { 2 | "/" : { 3 | view : function() { 4 | return m("div"); 5 | } 6 | }, 7 | "/2" : { 8 | view : function() { 9 | return m("div", { class : "class" }); 10 | } 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /transforms/m-route-vnodes/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // https://mithril.js.org/change-log.html#passing-vnodes-to-mmount-and-mroute 4 | // Converts raw vnodes passed to m.route into simple components 5 | module.exports = (file, api) => { 6 | var j = api.jscodeshift, 7 | s = api.stats; 8 | 9 | return j(file.source) 10 | .find(j.CallExpression, { 11 | callee : { 12 | object : { name : "m" }, 13 | property : { name : "route" } 14 | }, 15 | 16 | arguments : [{}, {}, { type : "ObjectExpression" }] 17 | }) 18 | .forEach((p) => j(p.get("arguments", 2)) 19 | .find(j.Property, { 20 | value : { 21 | callee : { name : "m" }, 22 | arguments : [{ type : "Literal" }] 23 | } 24 | }) 25 | .forEach(() => s("m.route(vnode)")) 26 | .forEach((p2) => p2.get("value").replace(j.template.expression` 27 | { 28 | view : function() { 29 | return ${p2.get("value").value}; 30 | } 31 | } 32 | 33 | `)) 34 | ) 35 | .toSource(); 36 | }; 37 | -------------------------------------------------------------------------------- /transforms/m-start-end-computation/_input.js: -------------------------------------------------------------------------------- 1 | m.startComputation(); 2 | m.endComputation(); 3 | -------------------------------------------------------------------------------- /transforms/m-start-end-computation/_output.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MithrilJS/mithril-codemods/44441518cf56d3491634243ec43d86233245c7bf/transforms/m-start-end-computation/_output.js -------------------------------------------------------------------------------- /transforms/m-start-end-computation/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // http://mithril.js.org/change-log.html#no-more-redraw-locks 4 | // Remove instances of m.startComputation()/m.endComputation() 5 | module.exports = (file, api) => { 6 | var j = api.jscodeshift, 7 | s = api.stats; 8 | 9 | return j(file.source) 10 | .find(j.CallExpression, { 11 | callee : { 12 | object : { name : "m" }, 13 | property : { name : (name) => (name === "startComputation" || name === "endComputation") } 14 | } 15 | }) 16 | .forEach(() => s("m.startComputation/m.endComputation")) 17 | .remove() 18 | .toSource(); 19 | }; 20 | -------------------------------------------------------------------------------- /transforms/m-sync/_input.js: -------------------------------------------------------------------------------- 1 | m.sync([ 2 | m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }), 3 | m.request({ method: 'GET', url: 'https://api.github.com/users/isiahmeadows' }), 4 | ]) 5 | .then(function (users) { 6 | console.log("Contributors:", users[0].name, "and", users[1].name); 7 | }); 8 | -------------------------------------------------------------------------------- /transforms/m-sync/_output.js: -------------------------------------------------------------------------------- 1 | Promise.all([ 2 | m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }), 3 | m.request({ method: 'GET', url: 'https://api.github.com/users/isiahmeadows' }), 4 | ]) 5 | .then(function (users) { 6 | console.log("Contributors:", users[0].name, "and", users[1].name); 7 | }); 8 | -------------------------------------------------------------------------------- /transforms/m-sync/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // https://mithril.js.org/change-log.html#msync-removed 4 | // Convert `m.sync(...).then(...)` into `Promise.all(...).then(...)` 5 | module.exports = (file, api) => { 6 | var j = api.jscodeshift, 7 | s = api.stats; 8 | 9 | return j(file.source) 10 | .find(j.CallExpression, { 11 | callee : { 12 | object : { name : "m" }, 13 | property : { name : "sync" } 14 | } 15 | }) 16 | .forEach(() => s("m.sync()")) 17 | // rewrite m.sync as Promise.all 18 | .replaceWith((p) => j.template.expression` 19 | Promise.all(${p.get("arguments").value}) 20 | `) 21 | .toSource(); 22 | }; 23 | -------------------------------------------------------------------------------- /transforms/query-string/_input.js: -------------------------------------------------------------------------------- 1 | m.route.buildQueryString({ 2 | s : "s" 3 | }); 4 | 5 | var parsed = m.route.parseQueryString("s=s"); 6 | 7 | wrapper(`query?${m.route.buildQueryString({ a : "b" })}`); 8 | -------------------------------------------------------------------------------- /transforms/query-string/_output.js: -------------------------------------------------------------------------------- 1 | m.buildQueryString({ 2 | s : "s" 3 | }); 4 | 5 | var parsed = m.parseQueryString("s=s"); 6 | 7 | wrapper(`query?${m.buildQueryString({ a : "b" })}`); 8 | -------------------------------------------------------------------------------- /transforms/query-string/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // https://github.com/tivac/mithril-codemods/issues/30 4 | // m.route.buildQueryString() => m.buildQueryString(); 5 | // m.route.parseQueryString() => m.parseQueryString(); 6 | module.exports = (file, api) => { 7 | var j = api.jscodeshift, 8 | s = api.stats; 9 | 10 | return j(file.source) 11 | .find(j.CallExpression, { 12 | callee : { 13 | object : { 14 | object : { name : "m" }, 15 | property : { name : "route" } 16 | }, 17 | property : (prop) => prop.name === "buildQueryString" || prop.name === "parseQueryString" 18 | } 19 | }) 20 | .forEach(() => s("m.route.buildQueryString()/m.route.parseQueryString()")) 21 | .replaceWith((p) => j.template.expression` 22 | m.${p.get("callee", "property", "name").value}(${p.get("arguments").value}) 23 | `) 24 | .toSource(); 25 | }; 26 | -------------------------------------------------------------------------------- /transforms/svg-namespace-xlink/_input.js: -------------------------------------------------------------------------------- 1 | m("svg", 2 | m("use[href='svg-attr.svg#icon']") 3 | ); 4 | 5 | m("svg.class", 6 | m("use", { 7 | href : "identifier-object.svg#icon" 8 | }) 9 | ); 10 | 11 | m("svg#id", 12 | m("use", { 13 | "href" : "literal-object.svg#icon" 14 | }) 15 | ); 16 | 17 | // Not transformed because unsure of context 18 | m("image[href='no-context-attr.gif']"); 19 | m("image", { 20 | href : "no-context-obj.gif" 21 | }); 22 | 23 | // Not transformed because no SVG parent 24 | m("div", 25 | m("a", { 26 | href : "no-parent-obj.jpg" 27 | }), 28 | m("image[href='no-parent-attr.gif']") 29 | ); 30 | -------------------------------------------------------------------------------- /transforms/svg-namespace-xlink/_output.js: -------------------------------------------------------------------------------- 1 | m("svg", 2 | m("use[xlink:href='svg-attr.svg#icon']") 3 | ); 4 | 5 | m("svg.class", 6 | m("use", { 7 | "xlink:href" : "identifier-object.svg#icon" 8 | }) 9 | ); 10 | 11 | m("svg#id", 12 | m("use", { 13 | "xlink:href" : "literal-object.svg#icon" 14 | }) 15 | ); 16 | 17 | // Not transformed because unsure of context 18 | m("image[href='no-context-attr.gif']"); 19 | m("image", { 20 | href : "no-context-obj.gif" 21 | }); 22 | 23 | // Not transformed because no SVG parent 24 | m("div", 25 | m("a", { 26 | href : "no-parent-obj.jpg" 27 | }), 28 | m("image[href='no-parent-attr.gif']") 29 | ); 30 | -------------------------------------------------------------------------------- /transforms/svg-namespace-xlink/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function href(j, p) { 4 | return ( 5 | j.match(p, { 6 | key : { name : "href" } 7 | }) || 8 | j.match(p, { 9 | key : { value : "href" } 10 | }) 11 | ); 12 | } 13 | 14 | function svg(j, p) { 15 | var parent = p.parent; 16 | 17 | while(parent && !j.match(parent, { 18 | type : "CallExpression", 19 | callee : { name : "m" }, 20 | arguments : [{ 21 | value : (v) => v.indexOf("svg") === 0 22 | }] 23 | })) { 24 | parent = parent.parent; 25 | } 26 | 27 | return parent; 28 | } 29 | 30 | // https://mithril.js.org/change-log.html#xlink-namespace-required 31 | // Adds `xlink` namespace to `href` attributes in SVGs 32 | module.exports = (file, api) => { 33 | var j = api.jscodeshift, 34 | s = api.stats, 35 | 36 | parsed = j(file.source); 37 | 38 | parsed 39 | // m("use/image", { href : "..." }) 40 | .find(j.CallExpression, { 41 | callee : { name : "m" }, 42 | arguments : [ 43 | { type : "Literal" }, 44 | { 45 | type : "ObjectExpression", 46 | properties : (props) => props.some(href.bind(null, j)) 47 | } 48 | ] 49 | }) 50 | // Walk up parents, searching for m("svg") 51 | .filter(svg.bind(null, j)) 52 | .forEach(() => s('m("use")/m("image")')) 53 | // Go through object properties and rename href to xlink:href 54 | .forEach((p) => 55 | p.get("arguments", 1, "properties") 56 | .filter(href.bind(null, j)) 57 | .forEach((prop) => 58 | prop.get("key").replace(j.literal("xlink:href")) 59 | ) 60 | ) 61 | .toSource(); 62 | 63 | return parsed 64 | // m("use[href='...']") 65 | .find(j.CallExpression, { 66 | callee : { name : "m" }, 67 | arguments : [ 68 | { 69 | type : "Literal", 70 | value : (v) => v.indexOf("[href") > -1 71 | } 72 | ] 73 | }) 74 | .filter(svg.bind(null, j)) 75 | .forEach(() => s('m("use href="..."])')) 76 | .forEach((p) => 77 | p.get("arguments", 0).replace(j.literal( 78 | p.get("arguments", 0).getValueProperty("value").replace("[href", "[xlink:href") 79 | )) 80 | ) 81 | .toSource(); 82 | }; 83 | -------------------------------------------------------------------------------- /transforms/unsafe-component-onunload/_input.js: -------------------------------------------------------------------------------- 1 | // Empty controller removal 2 | var c = { 3 | controller : function() { 4 | this.onunload = function() { 5 | console.log("onunload"); 6 | }; 7 | } 8 | }; 9 | 10 | // Leave controller 11 | var c = { 12 | controller : function() { 13 | this.prop = "value"; 14 | 15 | this.onunload = function() { 16 | console.log("onunload"); 17 | }; 18 | } 19 | }; 20 | 21 | // Arrow fns 22 | var c = { 23 | controller : function() { 24 | this.onunload = () => console.log("onunload"); 25 | } 26 | }; 27 | 28 | // Not using `this` 29 | var c = { 30 | controller : function() { 31 | var ctrl = this; 32 | 33 | ctrl.onunload = function() { 34 | console.log("onunload"); 35 | } 36 | } 37 | }; 38 | 39 | // Reference 40 | var c = { 41 | controller : function() { 42 | this.onunload = reference; 43 | } 44 | }; 45 | 46 | var c = { 47 | controller : function() { 48 | otherFunc(); 49 | 50 | this.onunload = reference; 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /transforms/unsafe-component-onunload/_output.js: -------------------------------------------------------------------------------- 1 | // Empty controller removal 2 | var c = { 3 | onremove: function() { 4 | console.log("onunload"); 5 | } 6 | }; 7 | 8 | // Leave controller 9 | var c = { 10 | controller : function() { 11 | this.prop = "value"; 12 | }, 13 | 14 | onremove: function() { 15 | console.log("onunload"); 16 | } 17 | }; 18 | 19 | // Arrow fns 20 | var c = { 21 | onremove: () => console.log("onunload") 22 | }; 23 | 24 | // Not using `this` 25 | var c = { 26 | controller : function() { 27 | var ctrl = this; 28 | }, 29 | 30 | onremove: function() { 31 | console.log("onunload"); 32 | } 33 | }; 34 | 35 | // Reference 36 | var c = { 37 | onremove: reference 38 | }; 39 | 40 | var c = { 41 | controller : function() { 42 | otherFunc(); 43 | }, 44 | 45 | onremove: reference 46 | }; 47 | -------------------------------------------------------------------------------- /transforms/unsafe-component-onunload/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // https://mithril.js.org/change-log.html#run-code-on-component-removal 4 | // Convert `controller.onunload` into `controller.onremove` 5 | module.exports = (file, api) => { 6 | var j = api.jscodeshift, 7 | s = api.stats; 8 | 9 | return j(file.source) 10 | .find(j.Property, { 11 | key : { 12 | name : (name) => name === "controller" || name === "oninit" 13 | }, 14 | value : j.FunctionExpression 15 | }) 16 | .forEach((prop) => { 17 | j(prop.get("value")) 18 | .find(j.AssignmentExpression, { 19 | operator : "=", 20 | 21 | left : { 22 | type : "MemberExpression", 23 | property : { name : "onunload" } 24 | } 25 | }) 26 | .forEach((p) => { 27 | var fn = p.get("right"); 28 | 29 | s("controller.onunload"); 30 | 31 | // Add onremove property 32 | prop.parent.get("properties").push(j.property( 33 | "init", 34 | j.identifier("onremove"), 35 | fn.get("value").node 36 | )); 37 | 38 | // Remove the assignment 39 | p.parent.replace(); 40 | 41 | // Check if the controller fn is now empty 42 | if(prop.get("value", "body", "body", "length").getValueProperty("value") === 0) { 43 | prop.replace(); 44 | } 45 | }); 46 | }) 47 | .toSource(); 48 | }; 49 | -------------------------------------------------------------------------------- /transforms/unsafe-component-wrapping/_input.js: -------------------------------------------------------------------------------- 1 | m("div", { 2 | view : function() { 3 | 4 | } 5 | }, {}); 6 | -------------------------------------------------------------------------------- /transforms/unsafe-component-wrapping/_output.js: -------------------------------------------------------------------------------- 1 | m("div", m({ 2 | view : function() { 3 | 4 | } 5 | }), {}); 6 | -------------------------------------------------------------------------------- /transforms/unsafe-component-wrapping/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // https://mithril.js.org/change-log.html#passing-components-to-m 4 | // Attempt to ensure that components as args to `m()` are wrapped in their own `m( { 6 | var j = api.jscodeshift, 7 | s = api.stats; 8 | 9 | return j(file.source) 10 | .find(j.CallExpression, { 11 | callee : { name : "m" } 12 | }) 13 | .forEach((p) => { 14 | // Walk function arguments, find things that look like components, wrap with `m()` 15 | j(p.get("arguments")) 16 | .find(j.ObjectExpression, { 17 | properties : [ 18 | { 19 | key : { 20 | name : "view" 21 | } 22 | } 23 | ] 24 | }) 25 | .forEach(() => s("Unwrapped component")) 26 | .replaceWith((p2) => j.template.expression` 27 | m(${p2.node}) 28 | `); 29 | }) 30 | .toSource(); 31 | }; 32 | -------------------------------------------------------------------------------- /transforms/unsafe-config/_input.js: -------------------------------------------------------------------------------- 1 | m("div", { 2 | config: function(el) { 3 | el.fooga; 4 | fooga.el; 5 | } 6 | }); 7 | 8 | m("div", { 9 | config: function(el) { 10 | "onupdate"; 11 | } 12 | }); 13 | 14 | m("div", { 15 | config : function(el, init) { 16 | if(!init) { 17 | "oncreate"; 18 | } 19 | } 20 | }); 21 | 22 | m("div", { 23 | config: function(el, init) { 24 | if(!init) { 25 | "oncreate"; 26 | 27 | return; 28 | } 29 | 30 | "onupdate"; 31 | } 32 | }); 33 | 34 | m("div", { 35 | config: function(el, init) { 36 | if(init) { 37 | return; 38 | } 39 | 40 | "oncreate"; 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /transforms/unsafe-config/_output.js: -------------------------------------------------------------------------------- 1 | m("div", { 2 | onupdate: function(vnode) { 3 | vnode.dom.fooga; 4 | fooga.el; 5 | } 6 | }); 7 | 8 | m("div", { 9 | onupdate: function(vnode) { 10 | "onupdate"; 11 | } 12 | }); 13 | 14 | m("div", { 15 | oncreate: function(vnode) { 16 | "oncreate"; 17 | } 18 | }); 19 | 20 | m("div", { 21 | onupdate: function(vnode) { 22 | "onupdate"; 23 | }, 24 | 25 | oncreate: function(vnode) { 26 | "oncreate"; 27 | return; 28 | } 29 | }); 30 | 31 | m("div", { 32 | oncreate: function(vnode) { 33 | "oncreate"; 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /transforms/unsafe-config/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var replace = require("../../lib/identifier.js").replace; 4 | 5 | // https://mithril.js.org/change-log.html#config-function 6 | // Rewrite all `config` function instances into either oncreate/onupdate 7 | module.exports = (file, api) => { 8 | var j = api.jscodeshift, 9 | s = api.stats; 10 | 11 | return j(file.source) 12 | .find(j.Property, { 13 | key : { name : "config" }, 14 | value : j.Function.check 15 | }) 16 | .forEach((p) => { 17 | var params = p.get("value", "params"), 18 | names = params.map((param) => param.getValueProperty("name")); 19 | 20 | s("config function"); 21 | 22 | // rename `config` to `onupdate` 23 | p.get("key").replace(j.identifier("onupdate")); 24 | 25 | // If if takes any params rewrite to be more accurate 26 | if(names.length) { 27 | params.replace([ j.identifier("vnode") ]); 28 | } 29 | 30 | // Check for 1st arg, `el`, and rewrite as `vnode.dom` 31 | // This one is done early so the rewrites are already done 32 | // before the potential oncreate/onupdate split down below 33 | if(names[0]) { 34 | replace(j, p.get("value"), names[0], j.template.expression` 35 | vnode.dom 36 | `); 37 | } 38 | 39 | // Check for 4th arg, `vdom`, and rewrite as `vnode` 40 | if(names[3]) { 41 | replace(j, p.get("value"), names[3], j.identifier("vnode")); 42 | } 43 | 44 | // Check for 3rd arg, `state`, and rewrite as `vnode.state` 45 | if(names[2]) { 46 | replace(j, p.get("value"), names[2], j.template.expression` 47 | vnode.state 48 | `); 49 | } 50 | 51 | // If it uses isInitialized need to split behavior somewhat 52 | if(names[1]) { 53 | j(p.get("value")) 54 | .find(j.IfStatement) 55 | .find(j.Identifier, { name : names[1] }) 56 | .forEach((p2) => { 57 | /* eslint consistent-return: off */ 58 | var conditional = j(p2).closest(j.IfStatement), 59 | 60 | fnBody = "onupdate", 61 | ifBody = "oncreate", 62 | negated; 63 | 64 | // Figure out if the init value has been negated somehow 65 | negated = j(p2).closest(j.UnaryExpression, { 66 | operator : "!" 67 | }); 68 | 69 | // Negated means rename `config` to `onupdate` 70 | // Use conditional body as `oncreate` 71 | if(negated.length) { 72 | fnBody = "oncreate"; 73 | ifBody = "onupdate"; 74 | } 75 | 76 | p.get("key").replace(j.identifier(ifBody)); 77 | 78 | // Filter out empty return if blocks, they're ignorable 79 | if(j.match(conditional, { 80 | consequent : { 81 | body : [{ type : "ReturnStatement", argument : null }] 82 | } 83 | })) { 84 | return conditional.remove(); 85 | } 86 | 87 | // Add the if statement body as a new hook function 88 | p.parent.get("properties").push(j.property( 89 | "init", 90 | j.identifier(fnBody), 91 | j.template.expression` 92 | function(vnode) { 93 | ${conditional.get("consequent", "body").value} 94 | } 95 | ` 96 | )); 97 | 98 | conditional.remove(); 99 | 100 | // Remove original hook if it's now empty 101 | if(p.get("value", "body", "body").getValueProperty("length") === 0) { 102 | p.replace(); 103 | } 104 | }); 105 | } 106 | }) 107 | .toSource(); 108 | }; 109 | -------------------------------------------------------------------------------- /transforms/unsafe-event-cancel-redraw/_input.js: -------------------------------------------------------------------------------- 1 | // Simple 2 | m("div", { 3 | onclick : function() { 4 | m.redraw.strategy("none"); 5 | } 6 | }); 7 | 8 | // Different Arg 9 | m("div", { 10 | onclick : function(f) { 11 | m.redraw.strategy("none"); 12 | } 13 | }); 14 | 15 | // Multiple Args 16 | m("div", { 17 | onclick : function(e, g) { 18 | m.redraw.strategy("none"); 19 | } 20 | }); 21 | 22 | // Shouln't be transformed 23 | function fooga() { 24 | m.redraw.strategy("none"); 25 | } 26 | -------------------------------------------------------------------------------- /transforms/unsafe-event-cancel-redraw/_output.js: -------------------------------------------------------------------------------- 1 | // Simple 2 | m("div", { 3 | onclick : function(e) { 4 | e.redraw = false; 5 | } 6 | }); 7 | 8 | // Different Arg 9 | m("div", { 10 | onclick : function(f) { 11 | f.redraw = false; 12 | } 13 | }); 14 | 15 | // Multiple Args 16 | m("div", { 17 | onclick : function(e, g) { 18 | e.redraw = false; 19 | } 20 | }); 21 | 22 | // Shouln't be transformed 23 | function fooga() { 24 | m.redraw.strategy("none"); 25 | } 26 | -------------------------------------------------------------------------------- /transforms/unsafe-event-cancel-redraw/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var on = /^on/i; 4 | 5 | // https://mithril.js.org/change-log.html#cancelling-redraw-from-event-handlers 6 | // Converts m.redraw.strategy("none") calls in functions accepting `e` to e.redraw = false; 7 | module.exports = (file, api) => { 8 | var j = api.jscodeshift, 9 | s = api.stats; 10 | 11 | return j(file.source) 12 | .find(j.CallExpression, { 13 | callee : { 14 | object : { 15 | object : { name : "m" }, 16 | property : { name : "redraw" } 17 | }, 18 | property : { name : "strategy" } 19 | }, 20 | 21 | arguments : [{ value : "none" }] 22 | }) 23 | .forEach((p) => { 24 | var fn, arg; 25 | 26 | fn = j(p).closest(j.Property, { 27 | key : { name : on.test.bind(on) }, 28 | value : j.Function.check 29 | }); 30 | 31 | // Has to have a parent function 32 | if(!fn.length) { 33 | return; 34 | } 35 | 36 | if(fn.get("value", "params").getValueProperty("length") > 0) { 37 | arg = fn.get("value", "params", 0).node; 38 | } else { 39 | arg = j.identifier("e"); 40 | 41 | fn.get("value", "params").push(arg); 42 | } 43 | 44 | s("m.redraw.strategy(\"none\")"); 45 | 46 | p.replace(j.assignmentExpression( 47 | "=", 48 | j.memberExpression( 49 | arg, 50 | j.identifier("redraw") 51 | ), 52 | j.literal(false) 53 | )); 54 | }) 55 | .toSource(); 56 | }; 57 | -------------------------------------------------------------------------------- /transforms/unsafe-view/_input.js: -------------------------------------------------------------------------------- 1 | m.mount(document.body, { 2 | view : function(ctrl, options) { 3 | ctrl.fooga; 4 | options.wooga; 5 | 6 | thing.options = options; 7 | thing.ctrl = ctrl; 8 | } 9 | }); 10 | 11 | // No-args shouldn't be changed 12 | m.mount(document.body, { 13 | view : function() { } 14 | }); 15 | 16 | // Should work w/ arrow fns 17 | m.mount(document.body, { 18 | view : (ctrl) => m("div", ctrl.fooga) 19 | }); 20 | 21 | // Should work on functions that take "ctrl" as their first param 22 | function view(ctrl) { 23 | return m("div", ctrl.fooga); 24 | } 25 | 26 | // Shouldn't re-transform if already ported 27 | m.mount(document.body, { 28 | view : function(vnode) { 29 | return m("div", vnode.state.fooga); 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /transforms/unsafe-view/_output.js: -------------------------------------------------------------------------------- 1 | m.mount(document.body, { 2 | view : function(vnode) { 3 | vnode.state.fooga; 4 | vnode.attrs.wooga; 5 | 6 | thing.options = vnode.attrs; 7 | thing.ctrl = vnode.state; 8 | } 9 | }); 10 | 11 | // No-args shouldn't be changed 12 | m.mount(document.body, { 13 | view : function() { } 14 | }); 15 | 16 | // Should work w/ arrow fns 17 | m.mount(document.body, { 18 | view : (vnode) => m("div", vnode.state.fooga) 19 | }); 20 | 21 | // Should work on functions that take "ctrl" as their first param 22 | function view(vnode) { 23 | return m("div", vnode.state.fooga); 24 | } 25 | 26 | // Shouldn't re-transform if already ported 27 | m.mount(document.body, { 28 | view : function(vnode) { 29 | return m("div", vnode.state.fooga); 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /transforms/unsafe-view/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // https://mithril.js.org/change-log.html#view-parameters 4 | // Finds functions w/ "ctrl" as the first param, or "view" properties that are functions 5 | // Convert the first argument to into `vnode` 6 | // Convert any access to the first param to use `vnode.state.` instead 7 | // Convert any access to the second param to use `vnode.attrs.` instead 8 | module.exports = (file, api) => { 9 | var j = api.jscodeshift, 10 | s = api.stats; 11 | 12 | return j(file.source) 13 | .find(j.Function) 14 | .filter((p) => 15 | // Function with ctrl as first param 16 | j.match(p, { 17 | params : [{ name : "ctrl" }] 18 | }) || 19 | // Property named "view" that is a function 20 | j.match(p.parent, { 21 | key : { name : "view" }, 22 | value : j.Function.check 23 | }) 24 | ) 25 | .forEach((p) => { 26 | var ctrl = p.get("params", 0), 27 | options = p.get("params", 1), 28 | arg1, arg2; 29 | 30 | s("view property w/ args"); 31 | 32 | // Rewrite `ctrl` into `vnode` and update references 33 | if(j.Identifier.check(ctrl.node)) { 34 | arg1 = ctrl.getValueProperty("name"); 35 | 36 | // Early-out if this has already been transformed 37 | if(arg1 === "vnode") { 38 | return; 39 | } 40 | 41 | ctrl.replace(j.identifier("vnode")); 42 | 43 | j(p.get("body").node) 44 | .find(j.Identifier, { name : arg1 }) 45 | .filter((p2) => (j.MemberExpression.check(p2.parent.node) ? 46 | p2.parent.get("object") === p2 : 47 | true 48 | )) 49 | .forEach(() => s("vnode.state")) 50 | .replaceWith(j.template.expression` 51 | vnode.state 52 | `); 53 | } 54 | 55 | // Remove `options` and update references 56 | if(j.Identifier.check(options.node)) { 57 | arg2 = options.getValueProperty("name"); 58 | 59 | options.replace(); 60 | 61 | j(p.get("body").node) 62 | .find(j.Identifier, { name : arg2 }) 63 | .filter((p2) => (j.MemberExpression.check(p2.parent.node) ? 64 | p2.parent.get("object") === p2 : 65 | true 66 | )) 67 | .forEach(() => s("vnode.attrs")) 68 | .replaceWith(j.template.expression` 69 | vnode.attrs 70 | `); 71 | } 72 | }) 73 | .toSource(); 74 | }; 75 | -------------------------------------------------------------------------------- /transforms/warning-m-deferred/_input.js: -------------------------------------------------------------------------------- 1 | var a = m.deferred(); 2 | 3 | // Existing comment 4 | m.deferred(); 5 | -------------------------------------------------------------------------------- /transforms/warning-m-deferred/_output.js: -------------------------------------------------------------------------------- 1 | var a = console.warn("m.deferred has been removed from mithril 1.0") || m.deferred(); 2 | 3 | // Existing comment 4 | console.warn("m.deferred has been removed from mithril 1.0") || m.deferred(); 5 | -------------------------------------------------------------------------------- /transforms/warning-m-deferred/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // https://mithril.js.org/change-log.html#mdeferred-removed 4 | // Add a warning above any usage of `m.deferred()` 5 | module.exports = (file, api) => { 6 | var j = api.jscodeshift, 7 | s = api.stats; 8 | 9 | return j(file.source) 10 | .find(j.CallExpression, { 11 | callee : { 12 | object : { name : "m" }, 13 | property : { name : "deferred" } 14 | } 15 | }) 16 | .forEach(() => s("m.deferred")) 17 | .replaceWith((p) => j.template.expression` 18 | console.warn("m.deferred has been removed from mithril 1.0") || ${p.value} 19 | `) 20 | .toSource(); 21 | }; 22 | -------------------------------------------------------------------------------- /transforms/warning-m-prop/_input.js: -------------------------------------------------------------------------------- 1 | var a = m.prop(5); 2 | 3 | m.prop(10); 4 | -------------------------------------------------------------------------------- /transforms/warning-m-prop/_output.js: -------------------------------------------------------------------------------- 1 | var a = console.warn("m.prop has been removed from mithril 1.0") || m.prop(5); 2 | 3 | console.warn("m.prop has been removed from mithril 1.0") || m.prop(10); 4 | -------------------------------------------------------------------------------- /transforms/warning-m-prop/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // https://mithril.js.org/change-log.html#mprop-removed 4 | // Add a warning around any usage of `m.prop()` 5 | module.exports = (file, api) => { 6 | var j = api.jscodeshift, 7 | s = api.stats; 8 | 9 | return j(file.source) 10 | .find(j.CallExpression, { 11 | callee : { 12 | object : { name : "m" }, 13 | property : { name : "prop" } 14 | } 15 | }) 16 | .forEach(() => s("m.prop")) 17 | .replaceWith((p) => j.template.expression` 18 | console.warn("m.prop has been removed from mithril 1.0") || ${p.value} 19 | `) 20 | .toSource(); 21 | }; 22 | -------------------------------------------------------------------------------- /transforms/warning-m-redraw-arg/_input.js: -------------------------------------------------------------------------------- 1 | m.redraw(true); 2 | m.redraw(foo || bar); 3 | m.redraw(foo()); 4 | -------------------------------------------------------------------------------- /transforms/warning-m-redraw-arg/_output.js: -------------------------------------------------------------------------------- 1 | console.warn("m.redraw ignores arguments in mithril 1.0") || m.redraw(true); 2 | console.warn("m.redraw ignores arguments in mithril 1.0") || m.redraw(foo || bar); 3 | console.warn("m.redraw ignores arguments in mithril 1.0") || m.redraw(foo()); 4 | -------------------------------------------------------------------------------- /transforms/warning-m-redraw-arg/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // https://mithril.js.org/change-log.html#synchronous-redraw-removed 4 | // Add a warning around any usage of `m.redraw()` that accepts an argument 5 | module.exports = (file, api) => { 6 | var j = api.jscodeshift, 7 | s = api.stats; 8 | 9 | return j(file.source) 10 | .find(j.CallExpression, { 11 | callee : { 12 | object : { name : "m" }, 13 | property : { name : "redraw" } 14 | }, 15 | arguments : (node) => node.length 16 | }) 17 | .forEach(() => s("m.redraw(arg)")) 18 | .replaceWith((p) => j.template.expression` 19 | console.warn("m.redraw ignores arguments in mithril 1.0") || ${p.value} 20 | `) 21 | .toSource(); 22 | }; 23 | -------------------------------------------------------------------------------- /transforms/warning-m-redraw-strategy/_input.js: -------------------------------------------------------------------------------- 1 | m.redraw.strategy("all"); 2 | m.redraw.strategy("diff"); 3 | m.redraw.strategy("none"); 4 | 5 | function event() { 6 | m.redraw.strategy("none"); 7 | } 8 | -------------------------------------------------------------------------------- /transforms/warning-m-redraw-strategy/_output.js: -------------------------------------------------------------------------------- 1 | console.warn("m.redraw.strategy() does not exist in mithril 1.0"); 2 | 3 | if(m.redraw.strategy) { 4 | m.redraw.strategy("all"); 5 | } 6 | console.warn("m.redraw.strategy() does not exist in mithril 1.0"); 7 | 8 | if(m.redraw.strategy) { 9 | m.redraw.strategy("diff"); 10 | } 11 | console.warn("m.redraw.strategy() does not exist in mithril 1.0"); 12 | 13 | if(m.redraw.strategy) { 14 | m.redraw.strategy("none"); 15 | } 16 | 17 | function event() { 18 | console.warn("m.redraw.strategy() does not exist in mithril 1.0"); 19 | 20 | if(m.redraw.strategy) { 21 | m.redraw.strategy("none"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /transforms/warning-m-redraw-strategy/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // Wraps m.redraw.strategy() calls in a conditional that checks for existence (to ease porting) 4 | // Also console.warns about their existence 5 | module.exports = (file, api) => { 6 | var j = api.jscodeshift, 7 | s = api.stats; 8 | 9 | return j(file.source) 10 | .find(j.ExpressionStatement, { 11 | expression : { 12 | callee : { 13 | object : { 14 | object : { name : "m" }, 15 | property : { name : "redraw" } 16 | }, 17 | property : { name : "strategy" } 18 | } 19 | } 20 | }) 21 | .forEach(() => s("m.redraw.strategy()")) 22 | .replaceWith((p) => j.template.statements` 23 | console.warn("m.redraw.strategy() does not exist in mithril 1.0"); 24 | 25 | if(m.redraw.strategy) { 26 | m.redraw.strategy(${p.get("expression", "arguments", 0).node}); 27 | } 28 | `) 29 | .toSource(); 30 | }; 31 | -------------------------------------------------------------------------------- /transforms/warning-prevent-unload/_input.js: -------------------------------------------------------------------------------- 1 | this.onunload = function(e) { 2 | if (condition) e.preventDefault() 3 | } 4 | 5 | // Existing comment 6 | this.onunload = function(e) { 7 | if (condition) e.preventDefault() 8 | } 9 | 10 | this.onunload = function(e) { 11 | // ... 12 | } 13 | -------------------------------------------------------------------------------- /transforms/warning-prevent-unload/_output.js: -------------------------------------------------------------------------------- 1 | /* WARNING: onunload cannot cancel unmounting in mithril@1.x */ 2 | this.onunload = function(e) { 3 | if (condition) e.preventDefault() 4 | } 5 | 6 | // Existing comment 7 | /* WARNING: onunload cannot cancel unmounting in mithril@1.x */ 8 | this.onunload = function(e) { 9 | if (condition) e.preventDefault() 10 | } 11 | 12 | this.onunload = function(e) { 13 | // ... 14 | } 15 | -------------------------------------------------------------------------------- /transforms/warning-prevent-unload/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // https://mithril.js.org/change-log.html#preventing-unmounting 4 | // Finds .onunload functions that can call e.preventDefault() and adds a warning 5 | module.exports = (file, api) => { 6 | var j = api.jscodeshift, 7 | s = api.stats, 8 | 9 | comment = j.commentBlock(" WARNING: onunload cannot cancel unmounting in mithril@1.x "); 10 | 11 | return j(file.source) 12 | .find(j.AssignmentExpression, { 13 | left : { 14 | type : "MemberExpression", 15 | property : { name : "onunload" } 16 | } 17 | }) 18 | .forEach((p) => { 19 | var found; 20 | 21 | j(p.getValueProperty("right")) 22 | .find(j.MemberExpression, { 23 | property : { name : "preventDefault" } 24 | }) 25 | .forEach(() => (found = true)); 26 | 27 | // No-op if the onunload handler has no preventDefault logic 28 | if(!found) { 29 | return; 30 | } 31 | 32 | s("onunload preventDefault"); 33 | 34 | p.value.comments = [ comment ]; 35 | }) 36 | .toSource(); 37 | }; 38 | --------------------------------------------------------------------------------